How To: Cross-Platform Builds¶
This guide explains how to protect your application for multiple operating systems and architectures.
Supported Platforms¶
| Platform ID | OS | Architecture | File Extension |
|---|---|---|---|
win-x64 |
Windows | x86_64 | .exe |
linux-x64 |
Linux | x86_64 | (none) |
linux-arm64 |
Linux | aarch64 | (none) |
mac-x64 |
macOS | Intel | .app |
mac-arm64 |
macOS | Apple Silicon | .app |
Strategy¶
Cross-platform protection requires:
- Building your application separately on each target platform (or using cross-compilation)
- Protecting each platform artifact individually with PyLocket
PyLocket bundles a platform-specific native runtime for each target.
Recommended Build Mode¶
The correct PyInstaller mode is platform-specific:
| Platform | PyInstaller Flags | Output | Why |
|---|---|---|---|
| macOS | --onedir --windowed |
.app |
Produces a proper .app bundle with Contents/Frameworks/Python.framework/ physically present (~6.5 MB dylib). The .app appears as a single file in Finder and is the standard format for macOS distribution. |
| Windows | --onefile --windowed |
.exe |
Single .exe with the Python runtime and all DLLs compressed inside. Extracts to a temp directory at launch. |
| Linux | --onefile |
binary | Single self-contained binary. Same extraction behaviour as Windows. |
Do not use --onefile on macOS
--onefile on macOS compresses everything — including the Python runtime and all native libraries — into a binary blob inside the executable. The resulting .app bundle does not contain Contents/Frameworks/Python.framework/ as a physical directory. Python is buried inside the compressed executable and unusable by the system. This produces a non-standard .app that can cause code-signature failures and "damaged" errors after protection. Always use --onedir --windowed for macOS.
Register All Platforms at Once¶
Build Per Platform¶
Option A: Build on Native Hardware¶
Build on each target platform using the recommended build mode:
# On macOS — always use --onedir --windowed
pyinstaller --onedir --windowed myapp.py
# → dist/MyApp.app (complete .app bundle with Python.framework in Contents/Frameworks/)
Option B: CI/CD Matrix Build¶
Use a CI/CD matrix to build on multiple runners:
# GitHub Actions
jobs:
build:
strategy:
matrix:
include:
- os: windows-latest
platform: win-x64
artifact: dist/myapp.exe
- os: ubuntu-latest
platform: linux-x64
artifact: dist/myapp
- os: macos-latest
platform: mac-arm64
artifact: dist/MyApp.app
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install pyinstaller -r requirements.txt
# Windows: single-file windowed binary
- if: runner.os == 'Windows'
run: pyinstaller --onefile --windowed myapp.py
# Linux: single-file binary
- if: runner.os == 'Linux'
run: pyinstaller --onefile myapp.py
# macOS: directory mode for a proper .app bundle
- if: runner.os == 'macOS'
run: pyinstaller --onedir --windowed myapp.py
- uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.platform }}
path: ${{ matrix.artifact }}
macOS CI artifact
The macOS --onedir --windowed step produces a dist/MyApp.app directory. upload-artifact handles directories natively, so no extra zipping is required in CI.
Protect Each Platform¶
# Windows
pylocket protect --app <APP_ID> --artifact dist/myapp.exe --platform win-x64 --python 3.12
# Linux x64
pylocket protect --app <APP_ID> --artifact dist/myapp --platform linux-x64 --python 3.12
# macOS ARM (.app bundle from --onedir --windowed)
pylocket protect --app <APP_ID> --artifact dist/MyApp.app --platform mac-arm64 --python 3.12
Each command produces a separate Build ID. Track them independently.
Uploading macOS .app bundles
When you upload a .app directory, the CLI automatically zips it for transfer. On the backend, PyLocket extracts the inner binary from Contents/MacOS/, protects it, and reassembles the full .app bundle with your Info.plist, icons, and frameworks intact.
Platform-Specific Native Runtime¶
Each platform requires its own native runtime library. The correct runtime is automatically bundled during protection. You do not need to manage this manually.
Licensing Across Platforms¶
License keys are platform-independent. A single license key works on any platform for the same app. Device fingerprints are platform-specific, so activating on Windows and macOS counts as two separate devices toward the device limit.
Delivery¶
When you have multiple platforms protected for the same app, the PyLocket delivery page automatically shows labeled download buttons for each available platform (e.g., "Windows", "macOS", "Linux"). Customers see only the platforms you have uploaded builds for.
Tip: For a full end-to-end walkthrough covering building, protecting, and distributing for all three OSes, see the Multi-OS Publishing Tutorial.
See Also¶
- Protect a PyInstaller Application — Detailed onefile vs. onedir guidance
- CI/CD Integration — Full CI/CD pipeline examples
- Supported Platforms — Complete platform matrix