Skip to content

Building the Executable

MrBeanDev edited this page Aug 23, 2026 · 1 revision

Building the Executable

Prebuilt binaries are on the Releases page. Build your own if you are on a platform not covered — an Intel Mac, for instance — or if you have changed the code.

PyInstaller cannot cross-compile. Build on the platform you are targeting.

Automatically, on a tag

.github/workflows/release.yml builds all three platforms and attaches them to a GitHub Release:

git tag -a v1.2.3 -m "Release 1.2.3"
git push origin v1.2.3

It also runs from the Actions tab via Run workflow, taking the tag as an input.

The matrix is Linux x86_64 on ubuntu-latest, macOS arm64 on macos-latest, and Windows x64 on windows-latest, each on Python 3.12. fail-fast is off, so one platform failing does not cancel the others. The whole run takes about four minutes.

By hand

cd face-detection-webapp/backend
.venv/bin/pip install pyinstaller
.venv/bin/pyinstaller --clean --noconfirm build-executable.spec

The result is dist/FaceGallery, or dist/FaceGallery.exe on Windows, at roughly 165 to 205 MB depending on platform. Most of that is dlib, its models, numpy, and OpenCV.

Why there is a separate requirements file

requirements.txt pins dlib, which compiles from source. That is slow and memory-hungry — fine once on a workstation, painful in CI.

requirements-build.txt uses dlib-bin instead, a prebuilt wheel providing the same dlib module. There is a catch: face_recognition declares a dependency on the distribution named dlib, so installing it normally drags in and compiles the real one even when dlib-bin is already present. The workflow sidesteps that:

pip install -r requirements-build.txt
pip install --no-deps face-recognition==1.3.0

requirements-build.txt supplies what --no-deps skips.

dlib-bin publishes wheels for Linux x86_64 and aarch64, Windows x64, and Apple Silicon. There is no x86_64 macOS wheel, which is why Intel Macs have no prebuilt binary.

What the spec does

build-executable.spec bundles the face_recognition_models .dat files, which PyInstaller does not find on its own because they are located through pkg_resources at runtime. It also lists the uvicorn protocol modules, which are imported dynamically and would otherwise be missed.

setuptools<81 must be present or pkg_resources cannot be imported at all.

Checking the result

BROWSER=true FACE_GALLERY_DATA_DIR=/tmp/fg-test ./dist/FaceGallery

BROWSER=true stops it launching a real browser. It should print the port it bound and the URL it would open. From another shell:

curl http://localhost:<port>/health

Expect {"status":"ok"}. The binary prefers port 8000 and falls back to an OS-assigned one if that is taken.

Signing

The released binaries are unsigned, so macOS Gatekeeper and Windows SmartScreen both warn on first run. Signing needs a paid Apple Developer account and a Windows code-signing certificate. The workarounds are in Running Your Own Backend.

Clone this wiki locally