-
-
Save srdjan-m/0bef8c5a11710e6e728869ef3d29fb7e to your computer and use it in GitHub Desktop.
PyInstaller spec template (written for PyInstaller 6.x)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- mode: python ; coding: utf-8 -*- | |
| # ============================================================================= | |
| # PyInstaller spec TEMPLATE (written for PyInstaller 6.x) | |
| # ----------------------------------------------------------------------------- | |
| # A .spec file is just a Python script that PyInstaller executes. You build with: | |
| # | |
| # pyinstaller template.spec # normal build | |
| # pyinstaller template.spec --noconfirm # overwrite dist/ without asking | |
| # pyinstaller template.spec --clean # wipe the PyInstaller cache first | |
| # | |
| # You do NOT pass the .py entry point on the command line when using a spec — | |
| # the entry point(s) are the `scripts` list inside Analysis() below. | |
| # | |
| # Two output layouts (pick ONE — see the EXE/COLLECT section near the bottom): | |
| # * ONE-FOLDER (default here): dist/<name>/ with the .exe + a _internal/ dir. | |
| # Faster startup (files are read in place, nothing is unpacked per run). | |
| # * ONE-FILE: dist/<name>.exe (single file). Convenient to distribute, but | |
| # every launch unpacks everything to a temp dir first -> slower to start. | |
| # | |
| # 6.x notes worth knowing: | |
| # * Bytecode encryption (the old `cipher` / `block_cipher`) was REMOVED in 6.0. | |
| # Don't use it; it's gone. (Old specs that still pass cipher=None are tolerated.) | |
| # * In one-folder builds the payload now lives in a `_internal/` subfolder | |
| # (renameable via COLLECT(contents_directory=...)). | |
| # * `optimize=` (Python -O/-OO level) is a real Analysis argument in 6.x. | |
| # ============================================================================= | |
| import sys | |
| # Optional helpers — very useful for third-party packages that ship data files, | |
| # hidden submodules, or their own DLLs/.so files. Uncomment when you need them. | |
| # from PyInstaller.utils.hooks import ( | |
| # collect_data_files, # grab a package's bundled data files | |
| # collect_submodules, # grab all submodules of a package (for dynamic imports) | |
| # collect_dynamic_libs, # grab a package's compiled libraries (.dll/.so/.dylib) | |
| # copy_metadata, # bundle a package's *.dist-info (needed by importlib.metadata) | |
| # ) | |
| # ----------------------------------------------------------------------------- | |
| # Handy variables (not required by PyInstaller — just keeps the spec tidy). | |
| # ----------------------------------------------------------------------------- | |
| APP_NAME = 'MyApp' | |
| ENTRY = 'main.py' | |
| ICON = 'assets/app.ico' # .ico on Windows, .icns on macOS, omit/None on Linux | |
| CONSOLE = True # True = keep a console window (see stdout/stderr). | |
| # False = windowed/GUI app, no console. | |
| # ----------------------------------------------------------------------------- | |
| # datas: DATA files to bundle. List of (source, dest_dir_inside_bundle) tuples. | |
| # * source may be a single file or a glob (e.g. 'assets/*.png'). | |
| # * dest is a FOLDER *inside* the bundle; '.' means the bundle root. | |
| # * At runtime resolve these via sys._MEIPASS (see the resource_path note at bottom). | |
| # ----------------------------------------------------------------------------- | |
| datas = [ | |
| ('assets/*.png', 'assets'), # -> _internal/assets/*.png | |
| ('assets/fonts/MyFont.ttf', '.'), # -> _internal/MyFont.ttf | |
| ('assets/models/*.dat', 'models'), # -> _internal/models/*.dat | |
| ('config.default.ini', '.'), # ship a default config next to code | |
| # From a third-party package (uncomment the helper import above): | |
| # *collect_data_files('some_package'), | |
| # *copy_metadata('some_package'), # if the app calls importlib.metadata | |
| ] | |
| # ----------------------------------------------------------------------------- | |
| # binaries: BINARY files (DLLs / .so / .dylib) to bundle, same (src, dest) form. | |
| # You usually DON'T need this — PyInstaller auto-detects most binary deps and | |
| # package hooks handle the rest. Use it for DLLs loaded at runtime by path, | |
| # or a private native lib the analysis can't see. | |
| # ----------------------------------------------------------------------------- | |
| binaries = [ | |
| # ('C:/path/to/some_runtime.dll', '.'), | |
| # *collect_dynamic_libs('some_package'), | |
| ] | |
| # ----------------------------------------------------------------------------- | |
| # hiddenimports: modules PyInstaller's static analysis can't find on its own — | |
| # typically imported dynamically (importlib, __import__, plugin systems, or | |
| # referenced only in strings). If the frozen app dies with ModuleNotFoundError, | |
| # add the missing module here. | |
| # ----------------------------------------------------------------------------- | |
| hiddenimports = [ | |
| # 'pkg_resources.py2_warn', | |
| # 'my_plugins.handler_a', | |
| # *collect_submodules('my_plugins'), # pull in a whole dynamically-loaded package | |
| ] | |
| a = Analysis( | |
| [ENTRY], # entry-point script(s). Multiple -> multiple executables. | |
| pathex=[], # extra dirs to add to sys.path during ANALYSIS (import search). | |
| binaries=binaries, # see above | |
| datas=datas, # see above | |
| hiddenimports=hiddenimports, # see above | |
| hookspath=[], # extra dirs holding your own hook-*.py files | |
| hooksconfig={}, # per-hook config, e.g. {'gi': {'module-versions': {...}}} | |
| runtime_hooks=[], # scripts executed *before* your entry point (env setup, patches) | |
| excludes=[], # modules to EXCLUDE — shrinks the build, e.g. | |
| # ['tkinter','matplotlib','pytest','PyQt5','test'] | |
| noarchive=False, # False: bundle .pyc into the PYZ archive (normal). | |
| # True: leave loose .pyc files (useful for debugging). | |
| optimize=0, # Python bytecode optimization: 0 (none), 1 (-O), 2 (-OO, | |
| # also strips docstrings). Leave 0 unless you need it. | |
| # module_collection_mode={ # 6.x: control how specific packages are collected | |
| # 'my_pkg': 'py', # 'py'=source, 'pyc'=bytecode, 'pyz'=in archive, | |
| # }, # 'pyz+py' etc. Rarely needed. | |
| win_no_prefer_redirects=False, # Windows SxS assembly handling — leave default. | |
| win_private_assemblies=False, # Windows SxS — leave default. | |
| ) | |
| # PYZ = the compressed archive of your pure-Python modules. | |
| pyz = PYZ(a.pure) | |
| # ----------------------------------------------------------------------------- | |
| # OPTIONAL: Splash screen (shown instantly while the app unpacks/starts). | |
| # Windows/Linux only (not macOS). Uncomment, then add `splash` and | |
| # `splash.binaries` to the EXE()/COLLECT() call as shown in comments below. | |
| # In your app, call pyi_splash.update_text(...) / pyi_splash.close() to control it. | |
| # ----------------------------------------------------------------------------- | |
| # splash = Splash( | |
| # 'assets/splash.png', | |
| # binaries=a.binaries, | |
| # datas=a.datas, | |
| # text_pos=(10, 50), | |
| # text_size=12, | |
| # text_color='black', | |
| # ) | |
| # ============================================================================= | |
| # OUTPUT LAYOUT — keep the ONE-FOLDER block OR the ONE-FILE block, not both. | |
| # ============================================================================= | |
| # ------------------------- ONE-FOLDER (recommended) -------------------------- | |
| # Produces dist/<APP_NAME>/<APP_NAME>.exe plus a _internal/ payload folder. | |
| # `exclude_binaries=True` keeps the binaries OUT of the exe so COLLECT can lay | |
| # them beside it (that's what makes startup fast). | |
| exe = EXE( | |
| pyz, | |
| a.scripts, | |
| # splash, # <- add if using a splash screen | |
| exclude_binaries=True, # one-folder: binaries handled by COLLECT below | |
| name=APP_NAME, | |
| debug=False, # True: verbose bootloader logging for diagnosing startup | |
| bootloader_ignore_signals=False, | |
| strip=False, # strip symbol tables (Linux/macOS); leave False on Windows | |
| upx=True, # compress binaries with UPX if it's on PATH (smaller, slower start) | |
| upx_exclude=[ # never UPX-compress these (compression can corrupt some DLLs) | |
| 'vcruntime140.dll', | |
| 'python3*.dll', | |
| 'qwindows.dll', | |
| ], | |
| console=CONSOLE, | |
| disable_windowed_traceback=False, # windowed apps: show a traceback dialog on crash | |
| argv_emulation=False, # macOS: turn dropped-file Apple events into argv | |
| target_arch=None, # macOS: 'x86_64', 'arm64', or 'universal2'. None = host arch. | |
| codesign_identity=None, # macOS code-signing identity | |
| entitlements_file=None, # macOS entitlements plist | |
| icon=ICON, | |
| contents_directory='_internal', # 6.x: name of the payload subfolder (default '_internal') | |
| # version='version_info.txt', # Windows version resource (see note at bottom) | |
| # manifest='app.manifest', # Windows: custom app manifest (e.g. request admin) | |
| # uac_admin=False, # Windows: True -> request elevation at launch | |
| ) | |
| coll = COLLECT( | |
| exe, | |
| a.binaries, | |
| a.datas, | |
| # splash.binaries, # <- add if using a splash screen | |
| strip=False, | |
| upx=True, | |
| upx_exclude=[], | |
| name=APP_NAME, # -> dist/<APP_NAME>/ | |
| ) | |
| # --------------------------- ONE-FILE (alternative) -------------------------- | |
| # Comment out the ONE-FOLDER block above and use this instead for a single .exe. | |
| # Note there's no COLLECT — a.binaries/a.datas go straight into the EXE, and | |
| # there is no exclude_binaries flag. | |
| # | |
| # exe = EXE( | |
| # pyz, | |
| # a.scripts, | |
| # a.binaries, | |
| # a.datas, | |
| # # splash, | |
| # # splash.binaries, | |
| # name=APP_NAME, | |
| # debug=False, | |
| # bootloader_ignore_signals=False, | |
| # strip=False, | |
| # upx=True, | |
| # upx_exclude=[], | |
| # runtime_tmpdir=None, # where the one-file bundle unpacks (None = OS temp) | |
| # console=CONSOLE, | |
| # disable_windowed_traceback=False, | |
| # argv_emulation=False, | |
| # target_arch=None, | |
| # codesign_identity=None, | |
| # entitlements_file=None, | |
| # icon=ICON, | |
| # # version='version_info.txt', | |
| # ) | |
| # ----------------------------- macOS .app bundle ----------------------------- | |
| # On macOS, wrap the one-file (or COLLECT) result in a proper .app bundle. | |
| # app = BUNDLE( | |
| # coll, # or `exe` for a one-file macOS build | |
| # name=f'{APP_NAME}.app', | |
| # icon=ICON, # use a .icns here | |
| # bundle_identifier='com.example.myapp', | |
| # version='1.0.0', | |
| # info_plist={ # extra Info.plist keys | |
| # 'NSHighResolutionCapable': True, | |
| # 'LSBackgroundOnly': False, | |
| # }, | |
| # ) | |
| # ============================================================================= | |
| # EXTRA NOTES | |
| # ----------------------------------------------------------------------------- | |
| # * Accessing bundled files at runtime — datas are NOT next to your .py when | |
| # frozen. Resolve paths through sys._MEIPASS: | |
| # | |
| # import sys, os | |
| # def resource_path(rel): | |
| # base = getattr(sys, '_MEIPASS', os.path.abspath('.')) | |
| # return os.path.join(base, rel) | |
| # # e.g. resource_path('assets/logo.png') | |
| # | |
| # `sys._MEIPASS` is the bundle root at runtime (the _internal/ dir for | |
| # one-folder, or the temp unpack dir for one-file). `getattr(sys,'frozen',False)` | |
| # tells you whether you're running frozen vs. from source. | |
| # | |
| # * Windows version resource (`version='version_info.txt'`): generate a starter | |
| # with `pyi-grab_version path\to\some.exe > version_info.txt`, then edit it. | |
| # | |
| # * MERGE(...) exists for building several apps that SHARE dependencies (so the | |
| # common DLLs/modules aren't duplicated across bundles). Niche; look it up if | |
| # you ship a suite of related executables. | |
| # | |
| # * Debugging a broken frozen build: | |
| # - build with console=True and run from a terminal to see the traceback; | |
| # - check build/<specname>/warn-<specname>.txt for missing-module warnings; | |
| # - ModuleNotFoundError at runtime -> add it to `hiddenimports`; | |
| # - missing data file -> add it to `datas` and load via resource_path(); | |
| # - a corrupt/broken DLL after UPX -> add it to `upx_exclude` (or set upx=False). | |
| # | |
| # * `--clean` (CLI) clears PyInstaller's cache; do it after changing deps or when | |
| # a build behaves oddly. The spec itself can't set --clean; pass it on the CLI. | |
| # ============================================================================= |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment