diff --git a/.gitignore b/.gitignore index 66e4ec7..e6ef33c 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ MANIFEST # PyInstaller *.manifest *.spec +!build/webdrop_bridge.spec # Installer logs pip-log.txt diff --git a/build/scripts/build_macos.sh b/build/scripts/build_macos.sh index 63b7534..d5d0e29 100644 --- a/build/scripts/build_macos.sh +++ b/build/scripts/build_macos.sh @@ -224,8 +224,7 @@ build_executable() { python3 -m PyInstaller \ --distpath="$DIST_DIR" \ - --buildpath="$TEMP_BUILD" \ - --specpath="$SPECS_DIR" \ + --workpath="$TEMP_BUILD" \ "$SPEC_FILE" if [ ! -d "$DIST_DIR/$APP_NAME.app" ]; then diff --git a/build/webdrop_bridge.spec b/build/webdrop_bridge.spec new file mode 100644 index 0000000..3c56159 --- /dev/null +++ b/build/webdrop_bridge.spec @@ -0,0 +1,108 @@ +# -*- mode: python ; coding: utf-8 -*- +"""PyInstaller specification for WebDrop Bridge. + +This spec is shared by Windows and macOS build scripts. +It bundles runtime resources (webapp, icons, translations) and an optional +build-time .env file injected via WEBDROP_ENV_FILE. +""" + +from pathlib import Path +import os + + +project_root = Path.cwd() +if not (project_root / "src").exists(): + project_root = Path(__file__).resolve().parents[1] + +pathex = [str(project_root / "src")] +entry_script = str(project_root / "src" / "webdrop_bridge" / "main.py") + +env_file = os.environ.get("WEBDROP_ENV_FILE") +bundle_id = os.environ.get("WEBDROP_BUNDLE_ID", "de.him_tools.webdrop-bridge") +bundle_version = os.environ.get("WEBDROP_VERSION", "0.0.0") + +asset_prefix = os.environ.get("WEBDROP_ASSET_PREFIX") +exe_name = os.environ.get("WEBDROP_EXE_NAME", asset_prefix or "WebDropBridge") +app_name = asset_prefix or exe_name + +icon_ico_default = project_root / "resources" / "icons" / "app.ico" +icon_icns_default = project_root / "resources" / "icons" / "app.icns" +icon_ico = Path(os.environ.get("WEBDROP_ICON_ICO", str(icon_ico_default))) +icon_icns = Path(os.environ.get("WEBDROP_ICON_ICNS", str(icon_icns_default))) + +datas = [ + (str(project_root / "resources"), "resources"), + (str(project_root / "webapp"), "webapp"), +] + +ui_dir = project_root / "src" / "webdrop_bridge" / "ui" +for js_file in ui_dir.glob("*.js"): + datas.append((str(js_file), "webdrop_bridge/ui")) + +if env_file and Path(env_file).exists(): + datas.append((env_file, ".")) + +hiddenimports = [ + "PySide6.QtWebEngineCore", + "PySide6.QtWebEngineWidgets", + "PySide6.QtWebChannel", +] + +a = Analysis( + [entry_script], + pathex=pathex, + binaries=[], + datas=datas, + hiddenimports=hiddenimports, + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) + +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name=exe_name, + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=str(icon_ico) if icon_ico.exists() else None, +) + +coll = COLLECT( + exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name=exe_name, +) + +app = BUNDLE( + coll, + name=f"{app_name}.app", + icon=str(icon_icns) if icon_icns.exists() else None, + bundle_identifier=bundle_id, + info_plist={ + "CFBundleName": app_name, + "CFBundleDisplayName": app_name, + "CFBundleShortVersionString": bundle_version, + "CFBundleVersion": bundle_version, + }, +) \ No newline at end of file