|
| 1 | +# Copyright (c) 2026 Nordic Semiconductor ASA |
| 2 | +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause |
| 3 | +# |
| 4 | +# Build a single self-contained Windows executable for the Data Forwarder Host. |
| 5 | +# |
| 6 | +# The result is ONE file, dist\data-forwarder-host.exe, that bundles the Python |
| 7 | +# interpreter and every dependency (PySide6/Qt6, numpy, bleak, ...). Copy it to |
| 8 | +# another Windows machine and run it directly - nothing has to be installed there. |
| 9 | +# |
| 10 | +# Usage (from the package directory or anywhere): |
| 11 | +# powershell -ExecutionPolicy Bypass -File .\scripts\build_windows_binary.ps1 |
| 12 | +# |
| 13 | +# Notes: |
| 14 | +# * Build on the OLDEST Windows version you intend to support (e.g. Windows 10 |
| 15 | +# or 11). Test on a clean VM before distributing broadly. |
| 16 | +# * Requires a standard Windows CPython 3.12+ on PATH (python.org or winget). |
| 17 | +# Do not use the nrfutil/NCS toolchain Python - use $env:PYTHON_BIN to point |
| 18 | +# at a normal install, e.g. py -3.12 |
| 19 | +# * Internet access for the one-time dependency download. |
| 20 | +# * Unsigned executables may trigger SmartScreen on first run; code-sign for |
| 21 | +# external distribution. |
| 22 | +# * BLE on Windows is implemented but not yet validated in project docs - test |
| 23 | +# UART and BLE after building. |
| 24 | + |
| 25 | +#Requires -Version 5.1 |
| 26 | +$ErrorActionPreference = "Stop" |
| 27 | + |
| 28 | +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path |
| 29 | +$PkgDir = Split-Path -Parent $ScriptDir |
| 30 | +Set-Location $PkgDir |
| 31 | + |
| 32 | +$PythonBin = if ($env:PYTHON_BIN) { $env:PYTHON_BIN } else { "python" } |
| 33 | +$BuildVenv = if ($env:BUILD_VENV) { $env:BUILD_VENV } else { Join-Path $PkgDir ".build-venv" } |
| 34 | +$VenvPython = Join-Path $BuildVenv "Scripts\python.exe" |
| 35 | + |
| 36 | +Write-Host ">> Using interpreter: $(& $PythonBin --version)" |
| 37 | +$InterpreterPath = & $PythonBin -c "import sys; print(sys.executable)" |
| 38 | +Write-Host ">> Interpreter path: $InterpreterPath" |
| 39 | +$Platform = & $PythonBin -c "import sys; print(sys.platform)" |
| 40 | +if ($Platform -ne "win32") { |
| 41 | + Write-Error "!! Expected Windows CPython (sys.platform=win32), got '$Platform'. Use a standard python.org install." |
| 42 | +} |
| 43 | + |
| 44 | +function Test-VenvUsable { |
| 45 | + if (-not (Test-Path -LiteralPath $VenvPython)) { |
| 46 | + return $false |
| 47 | + } |
| 48 | + & $VenvPython -m pip --version *> $null |
| 49 | + return $LASTEXITCODE -eq 0 |
| 50 | +} |
| 51 | + |
| 52 | +# 1. Isolated build environment (kept out of git via .gitignore). |
| 53 | +# Reuse only if python -m pip works; stale/copied venvs get recreated. |
| 54 | +if (-not (Test-VenvUsable)) { |
| 55 | + if (Test-Path -LiteralPath $BuildVenv) { |
| 56 | + Write-Host ">> Existing build venv is unusable (stale/foreign) - recreating" |
| 57 | + Remove-Item -LiteralPath $BuildVenv -Recurse -Force |
| 58 | + } |
| 59 | + Write-Host ">> Creating build venv at $BuildVenv" |
| 60 | + & $PythonBin -m venv $BuildVenv |
| 61 | + if ($LASTEXITCODE -ne 0) { |
| 62 | + Write-Error "!! Failed to create venv. Install Python 3.12+ with the venv module." |
| 63 | + } |
| 64 | +} |
| 65 | + |
| 66 | +# 2. Install the app's runtime deps (from pyproject.toml) + PyInstaller. |
| 67 | +Write-Host ">> Installing runtime dependencies and PyInstaller" |
| 68 | +& $VenvPython -m pip install --upgrade pip wheel *> $null |
| 69 | +if ($LASTEXITCODE -ne 0) { |
| 70 | + Write-Error "!! pip upgrade failed." |
| 71 | +} |
| 72 | +# pywin32-ctypes is required by PyInstaller on Windows (imported from compat.py). |
| 73 | +& $VenvPython -m pip install "." "pyinstaller>=6.6" "pywin32-ctypes>=0.2" |
| 74 | +if ($LASTEXITCODE -ne 0) { |
| 75 | + Write-Error "!! Dependency install failed." |
| 76 | +} |
| 77 | + |
| 78 | +# 3. Clean previous artefacts and build from the spec. |
| 79 | +# -I (isolated): the package root contains a `platform/` subpackage; without |
| 80 | +# isolation, CWD on sys.path shadows stdlib `platform` and PyInstaller dies |
| 81 | +# with "module 'platform' has no attribute 'win32_ver'". |
| 82 | +Write-Host ">> Building single-file binary" |
| 83 | +foreach ($dir in @("build", "dist")) { |
| 84 | + if (Test-Path -LiteralPath $dir) { |
| 85 | + Remove-Item -LiteralPath $dir -Recurse -Force |
| 86 | + } |
| 87 | +} |
| 88 | +& $VenvPython -I -m PyInstaller --noconfirm --clean data_forwarder_host.spec |
| 89 | +if ($LASTEXITCODE -ne 0) { |
| 90 | + Write-Error "!! PyInstaller build failed." |
| 91 | +} |
| 92 | + |
| 93 | +$Bin = Join-Path $PkgDir "dist\data-forwarder-host.exe" |
| 94 | +if (Test-Path -LiteralPath $Bin) { |
| 95 | + $sizeMb = [math]::Round((Get-Item -LiteralPath $Bin).Length / 1MB, 1) |
| 96 | + Write-Host "" |
| 97 | + Write-Host ">> Done. Single-file binary:" |
| 98 | + Write-Host " $Bin" |
| 99 | + Write-Host " size: ${sizeMb} MB" |
| 100 | + Write-Host ">> Run it with: $Bin" |
| 101 | +} else { |
| 102 | + Write-Error "!! Build finished but $Bin was not produced." |
| 103 | +} |
0 commit comments