Bug 1197325 -- Set volume icon for DMG in Linux->Mac cross compiles. r=ted
authorJustin Wood <Callek@gmail.com>
Mon, 30 Jan 2017 17:32:32 -0500
changeset 388086 ab096d7edae3081170cd4e46c9c7db174987f0fd
parent 388085 f9f33149db8af599d3af7ef7d55e7c6e0376da14
child 388087 e4c86796a0798c5af07c54e7cceca6044c2310a5
push id7198
push userjlorenzo@mozilla.com
push dateTue, 18 Apr 2017 12:07:49 +0000
treeherdermozilla-beta@d57aa49c3948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted
bugs1197325
milestone54.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 1197325 -- Set volume icon for DMG in Linux->Mac cross compiles. r=ted MozReview-Commit-ID: C4LFZB6msmL
browser/config/tooltool-manifests/macosx64/cross-releng.manifest
build/macosx/cross-mozconfig.common
moz.configure
python/mozbuild/mozpack/dmg.py
--- a/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
@@ -50,10 +50,18 @@
 },
 {
 "version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
 "size": 152573516,
 "digest": "eef2f10bf57005d11c34b9b49eb76d0f09d026295055039fea89952a3be51580abdab29366278ed4bfa393b33c5cee4d51d3af4221e9e7d7d833d0fc1347597c",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
+},
+{
+"size": 281576,
+"visibility": "public",
+"digest": "71616564533d138fb12f08e761c2638d054814fdf9c9439638ec57b201e100445c364d73d8d7a4f0e3b784898d5fe6264e8242863fc5ac40163f1791468bbc46",
+"algorithm": "sha512",
+"filename": "hfsplus-tools.tar.xz",
+"unpack": true
 }
 ]
--- a/build/macosx/cross-mozconfig.common
+++ b/build/macosx/cross-mozconfig.common
@@ -27,17 +27,19 @@ FLAGS="-target x86_64-apple-darwin10 -ml
 export CC="$topsrcdir/clang/bin/clang $FLAGS"
 export CXX="$topsrcdir/clang/bin/clang++ $FLAGS"
 export CPP="$topsrcdir/clang/bin/clang $FLAGS -E"
 export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config
 export LDFLAGS="-Wl,-syslibroot,$CROSS_SYSROOT -Wl,-dead_strip"
 export TOOLCHAIN_PREFIX=$CROSS_CCTOOLS_PATH/bin/x86_64-apple-darwin10-
 export DSYMUTIL=$topsrcdir/clang/bin/llvm-dsymutil
 export GENISOIMAGE=$topsrcdir/genisoimage/genisoimage
+export MKFSHFS=$topsrcdir/hfsplus-tools/newfs_hfs
 export DMG_TOOL=$topsrcdir/dmg/dmg
+export HFS_TOOL=$topsrcdir/dmg/hfsplus
 
 export HOST_CC="$topsrcdir/clang/bin/clang"
 export HOST_CXX="$topsrcdir/clang/bin/clang++"
 export HOST_CPP="$topsrcdir/clang/bin/clang -E"
 export HOST_CFLAGS="-g"
 export HOST_CXXFLAGS="-g"
 export HOST_LDFLAGS="-g"
 
--- a/moz.configure
+++ b/moz.configure
@@ -260,24 +260,30 @@ check_prog('DOXYGEN', ('doxygen',), allo
 check_prog('XARGS', ('xargs',))
 
 @depends(target)
 def extra_programs(target):
     if target.kernel == 'Darwin':
         return namespace(
             DSYMUTIL=('dsymutil', 'llvm-dsymutil'),
             GENISOIMAGE=('genisoimage',),
+            MKFSHFS=('newfs_hfs', 'mkfs.hfsplus'),
+            HFS_TOOL=('hfsplus',)
         )
     if target.os == 'GNU' and target.kernel == 'Linux':
         return namespace(RPMBUILD=('rpmbuild',))
 
 check_prog('DSYMUTIL', delayed_getattr(extra_programs, 'DSYMUTIL'),
            allow_missing=True)
 check_prog('GENISOIMAGE', delayed_getattr(extra_programs, 'GENISOIMAGE'),
            allow_missing=True)
+check_prog('MKFSHFS', delayed_getattr(extra_programs, 'MKFSHFS'),
+           allow_missing=True)
+check_prog('HFS_TOOL', delayed_getattr(extra_programs, 'HFS_TOOL'),
+           allow_missing=True)
 check_prog('RPMBUILD', delayed_getattr(extra_programs, 'RPMBUILD'),
            allow_missing=True)
 
 option('--enable-system-hunspell',
        help="Use system hunspell (located with pkgconfig)")
 
 @depends('--enable-system-hunspell', compile_environment)
 def check_for_hunspell(value, compile_env):
--- a/python/mozbuild/mozpack/dmg.py
+++ b/python/mozbuild/mozpack/dmg.py
@@ -1,21 +1,23 @@
 # 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 buildconfig
 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,60 +31,91 @@ 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):
+def set_folder_icon(dir, tmpdir):
     '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])
+    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):
+    '''
+    When cross compiling, we zero fill an hfs file, that we will turn into
+    a DMG. To do so we test the size of the staged dir, and add some slight
+    padding to that.
+    '''
+    if is_linux:
+        hfs = os.path.join(tmpdir, 'staged.hfs')
+        output = subprocess.check_output(['du', '-s', stagedir])
+        size = (int(output.split()[0]) / 1000)  # Get in MB
+        size = int(size * 1.02)  # Bump the used size slightly larger.
+        # Setup a proper file sized out with zero's
+        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):
+    '''
+    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.
+    '''
+    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:
         # 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')
+        hfs = os.path.join(tmpdir, 'staged.hfs')
         subprocess.check_call([
-            buildconfig.substs['GENISOIMAGE'],
-            '-V', volume_name,
-            '-D', '-R', '-apple', '-no-pad',
-            '-o', uncompressed,
-            stagedir
-        ])
+            buildconfig.substs['HFS_TOOL'], hfs, 'addall', stagedir])
         subprocess.check_call([
             buildconfig.substs['DMG_TOOL'],
-            'dmg',
-            uncompressed,
+            'build',
+            hfs,
             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))
@@ -95,27 +128,25 @@ def create_dmg(source_directory, output_
     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')
+        check_tools('DMG_TOOL', 'GENISOIMAGE', 'MKFSHFS', 'HFS_TOOL')
     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, ' '))
+        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)
+        set_folder_icon(stagedir, tmpdir)
         chmod(stagedir)
         create_dmg_from_staged(stagedir, output_dmg, tmpdir, volume_name)