ronaldoussoren/py2app

App compiled won't run on other Macs

Closed this issue · 5 comments

I have built an app using Tkinter and when I compile it using python setup.py py2app it works on my mac, but it won't work on other macs. This happen when I zip the folder dist and send it to another computer.

Can this be related to the architecture? ARM based now with the M1 chips, which wont work with x86?

The zip creates a DS_Store file:

zip -- "my_app" -- Contents/*
     |
     --- .DS_Store 

Traceback:

You can't open "my_app" because this application is not supported on this Mac.

This is how the setup.py looks like:

from setuptools import setup

class CONFIG:
    VERSION = 'v1.0.1'
    platform = 'darwin-x86_64'
    executable_stub = '/opt/homebrew/Frameworks/Python.framework/Versions/3.10/lib/libpython3.10.dylib' # this is important, check where is your Python framework and get the `dylib`
    APP_NAME = f'your_app_{VERSION}_{platform}'
    APP = ['main.py']
    DATA_FILES = [
        'config.json', 
        'countries_list.txt', 
        ('modules', ['modules/app.ico']),
        # this modules are automatically added if you use __init__.py in your folder
        # ('modules', ['modules/scraper_module.py']),
        # ('modules', ['modules/gui_module.py']),
    ]

    OPTIONS = {
        'argv_emulation': False,
        'iconfile': 'modules/app.ico',
        'plist': {
            'CFBundleName': APP_NAME,
            'CFBundleDisplayName': APP_NAME,
            'CFBundleGetInfoString': APP_NAME,
            'CFBundleVersion': VERSION,
            'CFBundleShortVersionString': VERSION,
            'PyRuntimeLocations': [
                executable_stub,
                # also the executable can look like this:
                #'@executable_path/../Frameworks/libpython3.4m.dylib',
            ]
        }
    }

def main():
    setup(
        name=CONFIG.APP_NAME,
        app=CONFIG.APP,
        data_files=CONFIG.DATA_FILES,
        options={'py2app': CONFIG.OPTIONS},
        setup_requires=['py2app'],
        maintainer='foo bar',
        author_email='foo@domain.com',
    )

if __name__ == '__main__':
    main()

This can be related to the architecture, but also on the macOS version.

  • You can check architecture support in the terminal (use "file dist/my_app.app/Contents/MacOS/my_app"), or in the Finder (get info on the app bundle should show the supported architectures)
  • The macOS version is harder to check because the launch executable used by py2app supports a wide selection of OS versions, but likely isn't the immediate problem

If you installed Python using an installers from www.python.org the Python interpreter supports macOS 10.9 or later, but most other distributions are far more targeted (AFAIK homebrew installs a Python that works on the OS version of your machine, or newer). This limitation is inherited by the app bundle you create, and that's made slightly worse by some wheels targeting fairly recent macOS versions as well.

Py2app will get a feature that reports on this in the future, but I don't know when.

Hi @ronaldoussoren , indeed with was related to the arch, bujt also to the python framework. Finally I did a short documentation that was what helped me to build the app successfully and distribute it between different macos. I'm not a mac dev, hope it is helpful for anyone else!

Deployment to create the app from setup.py in OSX

Instructions:

Virtual enviroment

Note: for each architecture we recommend to use a diferent virtual enviroment.

I. Python & VENV

  1. Look where is your python framework installed (install it directly from the package in https://www.python.org/downloads/macos/

  2. Create a virtualenviroment, activate it and install the dependencies

python3 venv env
source env/bin/activate
pip3 install -r requirements.txt

If the deployment is for x86_64 architecture, you can install the dependencies with the following command:

arch -x86_64 python -m pip install -r requirements.txt
arch -x86_64 python setup.py py2app --arch x86_64 # to build
  1. Go to your python framework folder (/Library/Frameworks/Python.framework/Versions/<version>/lib/) and copy the tcl8, tcl8.6 and tk8.6 into your virtualenv (env/lib/) folder.

II. Build the app

  1. Go to the setup.py and change the executable path for python (it is a variable called executable_stub) and change it for what's yours.
    For example:
executable_stub: "/Library/Frameworks/Python.framework/Versions/3.9/libpython3.9.dylib"
  1. In setup.py remember to check in the class CONFiG the version, the platform and architecture to deploy.
  2. Now you can execute: python3 setup.py py2app
  3. You have the new app created with all dependencies, including python framework, you can find this app in dist/<name>.app folder
  4. Execute nano /dist/<name>.app/Contents/Info.plist and change the PyRuntimeLocations to the one you have in APP
...
<key>PyRuntimeLocations</key>
<array>
        <string>@executable_path/../Frameworks/Python.framework/Versions/3.9/Python</string>
</array>
...
  1. Now you can execute the app in your terminal with ./dist/<name>.app/Contents/MacOS/<name> or double click in the app to test it.

Distribution in other Macs

Compress the app

Now you need to zip your <name>.app or the dist/ folder. The problem is in here, since zipping in the standard way this app is corrupting the files
There a couple of options that I think you can do:

  • open dist/, then in the Finder window that comes up, right click on the app and compress it

  • cd dist; zip -yr $NAME.app.zip $NAME.app

  • another option is to convert the <name.app> to a <name>.dmg file using your disk utility (you can find this option in the File menu) and then compress the .dmg file.
    observation: (if you use the zip command line tool, you must use the -y option) without it, your downloadable app bundle will be somewhat mysteriously broken even though the one before you zipped it will be fine.

    cc @ronaldoussoren

I'm reopening the issue because your write-up shows that something isn't working as it should, and that's something I want to investigate further.

In particular, py2app is supposed to do "II. Build the app" completely automatic, even for "Universal 2" apps that support both x86_64 and arm64. That said, for Universal 2 the success of this depends on which 3th-party libraries you use, as not all projects ship universal2 wheels (sadly).

This might be related to the usage of homebrew, that's not something I regularly test with as I don't use it myself. I prefer using the installers on python.org and wheels from PyPI, and don't have need for other software that's distributed through homebrew.

BTW. "zip -y..." is necessary because app bundles created by py2app contain symbolic links and zip won't include those as symbolic links without the -y flag.

@ronaldoussoren I see! Also I discover that if you are not a MacOS developer (for that you need to pay a subscription, etc). You can distribute the app in this way I meantion, but also you need to run sudo xattr -rd com.apple.quarantine <APP> in the shared machine. Otherwise it will not run, since apple will not found the registered developer (unkown developer warning).

Btw it is worth to mention that there's a docker-osx image that you can run different MacOS distribution! So you can simulate different OS enviroments. Of course will be always better do this directly from the shared machine, in case it uses a different architecture. But for anything else, the docker image is useful.