macospython-3.xcx-freezeesky

Create .app / .dmg with Esky


I'm developing a GUI app in Python. I use cx_Freeze to turn my app into .app/ / .dmg files for OS X users. So, for instance I can use python setup.py bdist_dmg to make cx_Freeze create a .dmg file that my users can use to install my app.

I now want my app to update itself automatically. Esky seems to be a promising framework for doing this. I can do python setup.py bdist_esky to create a version of my app that updates itself. It produces the following directory structure:

The top-level myapp is Esky's bootstrapping executable. It looks in the current directory, finds myapp-0.1/ as the latest version and then launches myapp-0.1/myapp.

How do I package this into a .dmg file which I can ship to my users? After my modifications to setup.py to get bdist_esky to work, bdist_dmg no longer works. The impression I get is that Esky is simply not meant to be used with bdist_dmg. Its documentation doesn't mention DMG files at all and I also couldn't find anything on Google.

As a first step, I tried to manually turn the files generated by Esky into an OS X .app/ bundle:

Info.plist contains the minimum amount of necessary information to get OS X to run myapp. When I try to run myapp.app however, I get:

Traceback (most recent call last):
  File "<string>", line 318, in bootstrap
  File "<string>", line 442, in get_best_version
FileNotFoundError: [Errno 2] No such file or directory: '/Users/michael/Temp/myapp.app/appdata'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<string>", line 689, in <module>
  File "<string>", line 335, in bootstrap
RuntimeError: no usable frozen versions were found

Looking at Esky's source code, it appears to have some special handling for OS X bundles in its appdir_from_executable(...) function. To make Esky happy, I tried to rearrange my files so appdata/ actually exists:

Unfortunately, this results in another error:

Traceback (most recent call last):
  File "<string>", line 689, in <module>
  File "<string>", line 336, in bootstrap
  File "<string>", line 363, in chainload
  File "<string>", line 425, in _chainload
UnboundLocalError: local variable 'exc_value' referenced before assignment

Is this really that difficult? Am I the only one who wants to use Esky and ship files to users in the (standard) .dmg format? What am I missing?


Solution

  • Michael, I saw your comment in the github issue as well. Let me answer it here.

    I had the same requirement and resolved in a similar way but I think I didn't use cx_Freeze. Another difference could be that I used Python 2.7.x. The best way to help you is to show you a few relevant files which worked well for me for creating .app and .dmg.

    1) The main setup file of the python project: https://dl.dropboxusercontent.com/u/13110611/temp/package_esky/setup_esky.py

    2) The utility script I used to prepare some data files etc https://dl.dropboxusercontent.com/u/13110611/temp/package_esky/prepare_setup.py

    3) The main shell script I used to create app and DMG. That is, this script will call everything else, and it's the only script I used from command line directly. https://dl.dropboxusercontent.com/u/13110611/temp/package_esky/package.sh

    4) The dmg-setup script which will create DMS from .app file. It's just a simple wrapper script to call create-dmg, an open souce project. https://dl.dropboxusercontent.com/u/13110611/temp/package_esky/dmg-setup

    Please try and let me know in case you have any other questions or issues.