pythonmacospyinstaller

What is wrong with this `.spec` file to generate a bundle for macOS?


I'm trying to generate a bundle for macOS for this project. The .spec file from the repository works fine on Windows, but when I try to use it for macOS, it fails.

I've tried to fix it for MacOS by modifying it, resulting in the following:

# -*- mode: python ; coding: utf-8 -*-
import os.path
from pathlib import Path
from PyInstaller.compat import is_darwin, is_win
from PyInstaller.utils.hooks import copy_metadata, collect_data_files, collect_all


datas = []

datas += collect_data_files("transformers", include_py_files=True)
datas += collect_data_files("lightning")
datas += collect_data_files("lightning_fabric")
datas += collect_data_files("speechbrain")
datas += collect_data_files("pyannote")
datas += collect_data_files("asteroid_filterbanks")
datas += collect_data_files("whisperx")
datas += collect_data_files("librosa")

datas += copy_metadata("tqdm")
datas += copy_metadata("regex")
datas += copy_metadata("requests")
datas += copy_metadata("packaging")
datas += copy_metadata("filelock")
datas += copy_metadata("numpy")
datas += copy_metadata("tokenizers")
datas += copy_metadata("pillow")
datas += copy_metadata("huggingface_hub")
datas += copy_metadata("safetensors")
datas += copy_metadata("pyyaml")

datas += [("res", "res")]
datas += [("config.ini", ".")]
datas += [(".env", ".")]
datas += [("venv/lib/python3.10/site-packages/customtkinter", "customtkinter")]
datas += [("venv/lib/python3.10/site-packages/torch", "torch")]
datas += [("venv/lib/python3.10/site-packages/torchgen", "torchgen")]
datas += [("venv/lib/python3.10/site-packages/speech_recognition", "speech_recognition")]
datas += [("venv/lib/python3.10/site-packages/moviepy", "moviepy")]
datas += [("venv/lib/python3.10/site-packages/dotenv", "dotenv")]
datas += [("venv/lib/python3.10/site-packages/openai", "openai")]

hiddenimports = [
    "huggingface_hub.repository",
    "pytorch",
    "sklearn.utils._cython_blas",
    "sklearn.neighbors.typedefs",
    "sklearn.neighbors.quad_tree",
    "sklearn.tree",
    "sklearn.tree._utils",
    "darkdetect",
    "tkinter.font",
    "tkinter.ttk",
    "cmath",
    "pickletools",
    "unittest.mock",
    "aifc",
    "proglog",
    "imageio",
    "decorator",
]

block_cipher = None

debug = True
if debug:
    options = [("v", None, "OPTION")]
else:
    options = []

a = Analysis(
    ["src/app.py"],
    pathex=[],
    binaries=[],
    datas=datas,
    hiddenimports=hiddenimports,
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)

pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

macos_icon = "res/macos/icon.icns"
windows_icon = "res/windows/icon.ico"

exe = EXE(
    pyz,
    a.scripts,
    options,
    exclude_binaries=True, #NEW
    name="Audiotext",
    debug=debug,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=debug,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file="res/macos/entitlements.plist" if is_darwin else None,
    icon=windows_icon if is_win else macos_icon,
)

coll = COLLECT(
    exe,
    a.binaries,
    a.zipfiles,
    a.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name="Audiotext",
)

app = BUNDLE(
    coll,
    name="Audiotext.app",
    icon=macos_icon,
    bundle_identifier="com.henestrosadev.audiotext",
    version="2.3.0",
    info_plist={
        "NSPrincipalClass": "NSApplication",
        "NSAppleScriptEnabed": False,
        "NSHighResolutionCapable": True,
        "NSMicrophoneUsageDescription": "Allow Audiotext to record audio from your microphone to generate transcriptions.",
    }
)

Now, it generates the bundle, but when I open the generated file, I always get a ModuleNotFoundError. For example, the error I have now is ModuleNotFoundError: No module named 'openai'. I've fixed it by adding this line, as I've done with te rest of the previous missing modules:

datas += [("venv/lib/python3.10/site-packages/openai", "openai")]

Now, the error I get is ModuleNotFoundError: No module named 'pydantic'. Something is wrong, because it doesn't make sense to manually add all of the program's packages.


Solution

  • The problem was related to the pathex, because for some reason PyInstaller only took the src directory of the project. I added the site-packages directory from the venv directory to the pathex.

    The file is now as follows:

    # -*- mode: python ; coding: utf-8 -*-
    import os.path
    from pathlib import Path
    from PyInstaller.compat import is_darwin, is_win
    
    import sys ; sys.setrecursionlimit(sys.getrecursionlimit() * 5)
    
    relative_site_packages_path = "venv/Lib/site-packages" if is_win else "venv/lib/python3.10/site-packages"
    
    datas = [
        (f"{relative_site_packages_path}/customtkinter", "customtkinter"),
        (f"{relative_site_packages_path}/transformers", "transformers"),
        (f"{relative_site_packages_path}/lightning", "lightning"),
        (f"{relative_site_packages_path}/lightning_fabric", "lightning_fabric"),
        (f"{relative_site_packages_path}/speechbrain", "speechbrain"),
        (f"{relative_site_packages_path}/pyannote", "pyannote"),
        (f"{relative_site_packages_path}/asteroid_filterbanks", "asteroid_filterbanks"),
        (f"{relative_site_packages_path}/whisperx", "whisperx"),
        (f"{relative_site_packages_path}/librosa", "librosa"),
        ("res", "res"),
        ("config.ini", "."),
        (".env", "."),
    ]
    
    hiddenimports = [
        "huggingface_hub.repository",
        "sklearn.utils._cython_blas",
        "sklearn.neighbors.quad_tree",
        "sklearn.tree",
        "sklearn.tree._utils",
    ]
    
    block_cipher = None
    
    debug = False
    if debug:
        options = [("v", None, "OPTION")]
    else:
        options = []
    
    a = Analysis(
        ["src/app.py"],
        pathex=[relative_site_packages_path],
        binaries=[],
        datas=datas,
        hiddenimports=hiddenimports,
        hookspath=[],
        hooksconfig={},
        runtime_hooks=[],
        excludes=[],
        win_no_prefer_redirects=False,
        win_private_assemblies=False,
        cipher=block_cipher,
        noarchive=False,
    )
    
    pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
    
    macos_icon = "res/macos/icon.icns"
    windows_icon = "res/windows/icon.ico"
    
    exe = EXE(
        pyz,
        a.scripts,
        options,
        exclude_binaries=True, #NEW
        name="Audiotext",
        debug=debug,
        bootloader_ignore_signals=False,
        strip=False,
        upx=True,
        upx_exclude=[],
        runtime_tmpdir=None,
        console=debug,
        disable_windowed_traceback=False,
        argv_emulation=False,
        target_arch=None,
        codesign_identity=None,
        entitlements_file="res/macos/entitlements.plist" if is_darwin else None,
        icon=windows_icon if is_win else macos_icon,
    )
    
    coll = COLLECT(
        exe,
        a.binaries,
        a.zipfiles,
        a.datas,
        strip=False,
        upx=True,
        upx_exclude=[],
        name="Audiotext",
    )
    
    app = BUNDLE(
        coll,
        name="Audiotext.app",
        icon=macos_icon,
        bundle_identifier="com.henestrosadev.audiotext",
        version="2.3.0",
        info_plist={
            "NSPrincipalClass": "NSApplication",
            "NSAppleScriptEnabed": False,
            "NSHighResolutionCapable": True,
            "NSMicrophoneUsageDescription": "Allow Audiotext to record audio from your microphone to generate transcriptions.",
        }
    )
    

    This solves the issue. However, I have another problem now because the Audiotext.app file won't open, while the Audiotext executable from the dist/Audiotext directory does. But it is not related to this topic, so I'll mark this as the answer.