# PyInstaller spec for the bundled Windows node daemon. # # Output: dist/meshbay-node/meshbay-node.exe (--onedir, relocatable) # Consumed by: packaging/win/build-node-runtime.ps1, which moves the tree to # packages/meshbay-client/node-runtime/ for electron-builder to carry as an # extraResource. # # The dependency set (aiortc, av, aioquic, pydantic-core, uvicorn, watchdog, # guessit, blake3, ...) has C/Rust extensions and does dynamic imports, so # each awkward package is pulled in whole with collect_all rather than left to # PyInstaller's static analysis. Add to COLLECT_ALL / HIDDEN / COPY_META when a # frozen run raises ModuleNotFoundError — that is the expected way this list grows. from PyInstaller.utils.hooks import collect_all, copy_metadata from PyInstaller.utils.win32.versioninfo import ( FixedFileInfo, StringFileInfo, StringStruct, StringTable, VarFileInfo, VarStruct, VSVersionInfo, ) # Version resource stamped into meshbay-node.exe so the Windows Firewall prompt, # Task Manager and the file's Properties read "MeshBay Node" rather than a bare # exe name. Read from the installed package so it cannot drift from pyproject. try: from importlib.metadata import version as _pkg_version _ver = _pkg_version("meshbay-node") except Exception: _ver = "0.0.0" _vtuple = tuple( int(p) for p in (_ver.split("+")[0].split(".") + ["0", "0", "0", "0"])[:4] ) _version_info = VSVersionInfo( ffi=FixedFileInfo(filevers=_vtuple, prodvers=_vtuple), kids=[ StringFileInfo([StringTable("040904B0", [ StringStruct("CompanyName", "MeshBay"), StringStruct("FileDescription", "MeshBay Node"), StringStruct("FileVersion", _ver), StringStruct("InternalName", "meshbay-node"), StringStruct("OriginalFilename", "meshbay-node.exe"), StringStruct("ProductName", "MeshBay Node"), StringStruct("ProductVersion", _ver), ])]), VarFileInfo([VarStruct("Translation", [0x0409, 1200])]), ], ) # Packages taken in full: data files + shared libs + every submodule. COLLECT_ALL = [ "meshbay_node", "meshbay_common", "aiortc", "aioice", "aioquic", "av", "cryptography", "pylibsrtp", "pyee", "google_crc32c", "pydantic", "pydantic_core", "fastapi", "starlette", "uvicorn", "anyio", "sniffio", "h11", "websockets", "httpx", "httpcore", "watchdog", "guessit", "rebulk", "babelfish", "mutagen", "PIL", "blake3", "msgpack", "zstandard", "aiosqlite", "jwt", "dns", "tzdata", ] # importlib.metadata lookups that survive freezing only if the dist-info is copied. COPY_META = [ "meshbay-node", "meshbay-common", "guessit", "rebulk", "babelfish", "uvicorn", "fastapi", ] # Imports PyInstaller's static pass misses even with collect_all. HIDDEN = [ "uvicorn.logging", "uvicorn.loops.auto", "uvicorn.loops.asyncio", "uvicorn.protocols.http.auto", "uvicorn.protocols.http.h11_impl", "uvicorn.protocols.websockets.auto", "uvicorn.protocols.websockets.websockets_impl", "uvicorn.lifespan.on", ] datas, binaries, hiddenimports = [], [], list(HIDDEN) for pkg in COLLECT_ALL: d, b, h = collect_all(pkg) datas += d binaries += b hiddenimports += h for dist in COPY_META: datas += copy_metadata(dist) a = Analysis( ["node-entry.py"], pathex=[], binaries=binaries, datas=datas, hiddenimports=hiddenimports, hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=["tkinter", "pytest", "ruff", "piexif", "IPython"], noarchive=False, ) pyz = PYZ(a.pure) exe = EXE( pyz, a.scripts, [], exclude_binaries=True, name="meshbay-node", debug=False, bootloader_ignore_signals=False, strip=False, upx=False, console=True, disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file=None, icon="../../packages/meshbay-client/build/icon.ico", version=_version_info, ) coll = COLLECT( exe, a.binaries, a.datas, strip=False, upx=False, upx_exclude=[], name="meshbay-node", )