in HowTo

Packaging a Mac OS X Application Using a DMG

I learned early on in my software career that packaging up a release of an application can be a real pain. Not only is it time consuming, but it is quite easy to forget a file or miss something when there are so many steps involved and you have to do it frequently. The solution to this is to automate the process. Putting together scripts to package up a release helps avoid missing steps and speeds up development and deployment of releases and updates.

One of the challenges of automating this on Mac OS X is figuring out how to script the creation of Apple disk image (DMG) files. Getting the arguments to hdiutil correct can be quite a challenge! In this post, I’ll give an example of a script I use to do the following:

  • Copy all the necessary files to a staging area
  • Strip and compress the executable files and libraries
  • Create a DMG of the correct size for the release
  • Add a link to /Applications to the DMG
  • Add a background to the DMG so when it opens up your company logo or application graphic appears
  • Check the background image’s DPI to ensure it is 72. Fix it if it’s not.
  • Resize the window and icons, and position items in the DMG so when it opens everything is in the right place
  • Create a final, compressed DMG

Packaging An Application Using a DMG

Packaging An Application Using a DMG

Let’s take a look…

I like to create a double-clickable file to run the bash script so I can just run it from the Finder instead of using Terminal. To do that, simply name the file with the extension .command and make sure it is executable (run “chmod +x fileName.command” in the terminal).

(These are the files used in this example: Packaging_Example.zip. They are also available at the end of this post.)

This first part of the bash script sets up the names and locations of files we’ll be using throughout. If you are going to use this script for your own project, you’ll need to change several of the variables near the top.

This script assumes that the .command file, SuperCoolApp.app, and Background.png are all in the same folder. It also assumes that the actual executable in the application bundle (in Contents/MacOS ) is the same name as the .app.

After the setup, the first thing we need to do is check the background image’s dots-per-inch (DPI) settings. On the Mac, anything other than 72 DPI will result in a distorted background image when opening the DMG on Mac OS X 10.7+.

Background image with DPI of 90 on Mac OS X 10.8

Background image with DPI of 90 on Mac OS X 10.8

To fix this, we use the built-in scriptable image processing system command line tool (sips) to check the DPI and convert the image file if necessary.

As Brett noted in the comments, some versions of the sips tool seem to have a bug that will not convert the DPI if the format is JPEG. So you may need to use a workaround or modify the script to output a different format from the input file.

Next we remove any old data, create the staging directory, and copy over all the stuff we want in the final DMG file.

Now we strip the symbols out of our executable and, if we have UPX in our path, we run it on the executable as well. (If you aren’t already using UPX, you should take a look. In most cases it will reduce the file size by quite a bit.)

Next, we figure out how big our temporary DMG needs to be by looking at the size of the staging directory, create the DMG, and mount it. The sleep command at the end simply waits for 2 seconds to make sure it’s mounted.

Now we set up the contents the way we want them to appear when the DMG is mounted by users. We add a link to /Applications so they can simply drag SuperCoolApp.app over to it, add a background image, and tell the Finder to set up the window size, icon sizes, and icon positions the way we want them.

In this final section, we unmount the temporary DMG, convert it to a compressed, final DMG, and clean up our temporary files.

Running this script by double-clicking the .command file opens Terminal and should give results something like this:

As with most developer projects, this could use some more error checking… If you release versions of your software frequently, you might want to read the version number from the .plist file instead of having to change it in this script. This is left as an exercise for the reader.

Please let me know in the comments if you find it useful or have any suggestions for improvements!

9 Sep 16: Added a note about the bug in Apple’s sips tool.

9 Nov 14: You may now find it as a gist on github as well.

7 Nov 13: Based on an issue Damien Cassou reported (see comments), added code to check the background image’s DPI and fix it if necessary.

Project Files: Packaging_Example.zip

Leave a Reply for jonathan Cancel Reply

Write a Comment

Comment

29 Comments

  1. Thanks for the script! Saved me time and money… I added a couple of lines for codesigning and notarization to get it already for distribution.

    • You’re welcome! Glad it helped.

      I added a couple of lines for codesigning and notarization to get it already for distribution.

      Yeah – I wrote this long before they were a thing πŸ˜ƒ. If I ever add it I’ll update this post.

  2. Andy – thank you so much, It worked right out of the box and saved me a good deal of time having to reinvent this! My new software, SceneWizard is one more step nearer to being released. I just have to figure out the Windows version – sigh! Did anybody ever tell you it is much easier being a computer science professor than a developer! (Well it was for me).

  3. Hey,

    Need some help in running post script command should open the app after we drag and drop.

    We can achieve this in package installer, not sure how to proceed the same with dmg, after the user drag and drop, just want to open the app once.

    Let us know

  4. Thks for your script and all your explanation, it is really useful!

    I had a small issue but I am pretty sure the cause is not your script.

    Sometimes when creating the dmg, the background image wasn’t stored or the .DS_Store wasn’t created. I spent a lot of time to try to figure it out. And eventually I got the error message “no space left on device”. It was really strange because the information about the dmg indicates enough of space and inodes to add extra files.
    It wasn’t even possible to include an empty extra file.

    To bypass the issue, I add an empty DS_Store file and the background image directly in the staging directory.

    I think it is maybe a bug in the osx dmg management, I am really interested if you have more information about this.

    • Jonathan:

      I have not run into this yet. One thing I can think of is that the SIZE calculation might be incorrect. When I look at it now, the way I’ve done it feels kind of hacky. I should probably just use stat instead.

      I’ve only run this script on 10.9 and 10.10. Are you on 10.11?

      • I’m not sure what could be happening, but one quick solution might be to modify the SIZE calculation. Instead of only adding 1M, add 2M.

        Is you folder/project at least 1M? If not the SIZE calculation will not work properly. As I mentioned above, this calculation feels like a hack and should be done more intelligently.

  5. Hello.
    here is my las comment :o)

    I added those lines to your script :

    just after that line :

    it added an icon, to the dmg file, when it is opened on the desktop.
    the icon file is a classic .icns file (there is several tutorial on internet on how to do it)

    thanks
    Olivier

  6. Super powerful script!

    I suggest to read the app version directly from app. This way you don’t need to modify the script when you package a new version into a DMG file. Here’s I I do it:

    I renamed VERSION to APP_VERSION to match the naming scheme from the other computed variable APP_EXE

    • That’s a great idea Lars! This simplifies things and removes a potential source of error.

      The renaming of the variable is also quite appropriate.

      Thanks for sharing.

  7. Due to the need for backwards compat with Lion, I decided to switch to a Node.js project called node-appdmg.

    For developers that are already using MacPorts or homebrew, installing node and npm is just a few commands.

  8. Hello, Awesome Script!
    We did just run into a problem when running the script on a non english Mac.
    We solved it by adding
    LANG=en_GB
    to the top of the script.
    This info might help some people figuring out why it does not work for them (The calculation of the dmg size fails because we are using , as comma)

    Thanks again,
    Matthias

  9. Ok, the fix was to change + 1.0 to + 5.0 when it’s padding the file size (2.0 may have worked too, but this should work good enough to commit to our master branch) πŸ™‚

    Thanks again. This script was a huge help without a lot of bloat or reading. πŸ™‚

    -Tres

  10. Hi, Thanks for this fantastic script!
    It was working fantastically for me and then suddenly stopped yesterday. This is the cryptic message I get:

    could not access /Volumes/LMMS 1.0.0/LMMS.app/Contents/share/
    lmms/themes/default/stepper-right.png - Operation canceled
    hdiutil: create failed - Operation canceled
    Created DMG: LMMS 1.0.0-temp.dmg
    hdiutil: attach failed - No such file or directory
    Add link to /Applications
    -bash: pushd: /Volumes/LMMS 1.0.0: No such file or directory
    ln: ./Applications: File exists
    -bash: popd: directory stack empty
    mkdir: /Volumes/LMMS 1.0.0: No such file or directory
    cp: directory /Volumes/LMMS 1.0.0/.background does not exist
    69:73: execution error: Finder got an error: Can’t get disk "LMMS 1.0.0". (-1728)
    hdiutil: detach failed - No such file or directory
    Creating compressed image
    hdiutil: convert failed - No such file or directory
    Done.

    • Tres:

      It’s hard to tell from that where it’s failing. Obviously this is one of those cases I mention at the end of the article that should have better error checking… Should error out instead of trying to continue.

      Did it create the staging dir, strip, and compress it properly?

      If you contact me through the contact page we can exchange emails and then I can see the entire output of the script. Might help narrow it down.

  11. Thank you very much. This script helped me a lot. If you are still making changes to the script, you might want to share a gist (http://gist.github.com/) or something. I have one problem though: I can’t make a PNG that looks good as a background in the Finder: it looks like the finder tries to resize my picture, the finder adds a white margin on the right. I took the exact same resolution as your template, but yours always looks good whereas mine always looks bad.

    • Damien:

      You’re welcome! Glad you found it useful.

      I haven’t been making any changes, but thanks for the gist suggestion. I’ll take a look.

      I’m not quite sure about the problem you’re seeing. I’ve never had the Finder resize my backgrounds. Maybe you can send me the image you’re using and I can try it here? We can connect through my contact page.

    • Just a note to point out that the problem Damien was seeing had to do with the background image’s DPI.

      I added code to check the DPI and fix it if necessary and have updated the post and zip file download above.

Webmentions

  • Apple:Using osacript to access Finder from crontab (or from non-interactive session) – Apple Questions 07 Nov 2013

    Just a note to point out that the problem Damien was seeing had to do with the background image’s DPI.

    I added code to check the DPI and fix it if necessary and have updated the post and zip file download above.

  • Using osacript to access Finder from crontab (or from non-interactive session) | Question and Answer 07 Nov 2013

    Just a note to point out that the problem Damien was seeing had to do with the background image’s DPI.

    I added code to check the DPI and fix it if necessary and have updated the post and zip file download above.