This script creates Mac OS X apps from a pip- or conda-installable package. The resulting app includes a complete conda environment and is NOT frozen (as would be created with something like pyinstaller).
The inspiration (and all of the credit) for this strategy comes from this blog post by Daniel Schreij, where he describes using this approach to bundle the OpenSesame app. See also his scripts on github.
The main difference between this package and
Daniel's is that this one
has no project-specific code in it and does not depend on biplist
or
dmgbuild
. It has been generalized so that it can bundle any package(s) that
can be pip installed. For instance, the following will create a bundled version
of napari, a fast n-dimensional image
viewer:
python bundle_osx.py napari
It is also fully self-contained, and requires no pre-existing conda environment. As such, it can be run on continuous integration platforms and will download all necessary conda and pip resources to build a Mac app given the name of a pip package.
As Daniel points out in his blog post and script readme there are a few pros and cons to this approach:
- The resulting app will not be frozen, making it easier to maintain, update, and extend (for instance, to add plugins or other packages after bundling)
- The bundled environment will behave much more like your development
environment. Whereas pyinstaller dynamically analyzes your program and
includes only the compiled bytecode and libraries required to run the
program, this approach literally copies a complete conda environment into the
Mac
.app/Contents/Resources
folder.
- This is a rather unconventional way of bundling an app, and probably would not comply with Apple's guidelines for app packges. As such, it would likely be difficult to get an app like this into the app store, and your users will probably always see the scary "This app cannot be opened because the developer cannot be verified" message. To open the program, they will need to go into Preferences > Security and explicitly allow the app.
- This creates much larger apps than pyinstaller.
Basic usage:
python bundle_osx.py napari
Include an app icon:
python bundle_osx.py napari -i path/to/icon.icns
Bundle an app using something other than the main pip package, such as the default branch of a git repository:
python bundle_osx.py napari --pip-install git+https://github.com/napari/napari.git
Bundle an app then run a quick program or test prior to code signing and DMG formation
python bundle_osx.py napari --test "napari --info"
Bundle together multiple pip installable apps into a custom app package:
python bundle_osx.py myapp --pip-install numpy scipy matplotlib
⚠️ TODO: there still needs to be a main "entry point" script... so this particular example wouldn't be that useful. Will need to add an--entry-point
argument tobundle_osx.py
in order for this type of thing to be useful.
$ python bundle_osx.py --help
usage: bundle_osx.py [-h] [-y] [-i] [--distpath] [--buildpath] [--py] [--nodmg]
[--pip-install [[...]]] [--conda-include [[...]]] [--conda-exclude [[...]]]
[--log-level] [--clean] app_name
positional arguments:
app_name Name of app to bundle. If '--pip-install' is not
specified, this name is also assumed to be a
pip-installable package.
optional arguments:
-h, --help show this help message and exit
-y, --noconfirm Replace existing app and resources without asking
for confirmation
-d, --nodmg Do not package app into .dmg file. By default a
DMG will be created
-i PATH, --icon PATH Icon file (in .icns format) for the bundle.
--distpath PATH Where to put the bundled app (default: ./dist)
--buildpath PATH Where to put build resources (default: ./build)
--py VERSION Python version to bundle. (default 3.8)
--pip-install [ ] Install these pip packages. Multiple arguments
accepted as would be passed to pip install. If not
provided, will attempt to `pip install <app_name>`
using `app_name` argument. If '--pip-install' IS
provided, then 'app_name' will NOT be installed
unless explicitly included in this list.
--conda-include [ ] directories in conda environment to include when
bundling
--conda-exclude [ ] glob patterns (from base conda environment) to
exclude when bundling
--cert-name KEY Optional name of certificate in keychain with which
to sign app. By default, uses ad-hoc code signing.
Pass "" to skip signing altogether.
--test [ [ ...]] Optional test commands to run after app bundling,
but before code signing and dmg formation.
--log-level LEVEL Amount of detail in build-time console messages.
may be one of TRACE, DEBUG, INFO, WARN, ERROR,
CRITICAL (default: WARN)
--clean Delete all folders created by this bundler, then exit.
--make-dmg APP_PATH Bundle prebuilt .app into a DMG, then exit.