Skip to the content.

Build Beam on macOS

This document explains how a collaborator can build Beam on a modern Mac, package the result, and send the finished artifact back.

The standard macOS output is a .app bundle zipped with ditto. That is the format to hand back for testing or release review.

Target

Build Standard

Use this standard unless there is a specific reason not to:

What The Collaborator Needs

Step 1: Install Xcode Command Line Tools

Open Terminal and run:

xcode-select --install

If the tools are already installed, macOS will say so.

Step 2: Install Python 3.12 From python.org

Install a 3.12.x macOS universal2 installer from Python.org.

After installation, verify it:

python3.12 --version
file /Library/Frameworks/Python.framework/Versions/3.12/bin/python3.12

Expected result:

Do not use a single-architecture Python if the goal is one archive for both Apple Silicon and Intel.

Step 3: Get The Source Code

Either clone the repository:

git clone https://github.com/MrNidnan/beam-project.git
cd beam-project

Or download the source archive from GitHub, extract it, and cd into the extracted folder.

Step 4: Confirm The Beam Version

Beam version comes from resources/json/strings.json.

Check it:

python3.12 - <<'PY'
import json
from pathlib import Path
version = json.loads(Path('resources/json/strings.json').read_text(encoding='utf-8'))['version']
print(version)
print('v' + version.removesuffix('.0'))
PY

Example output:

0.9.1.0
v0.9.1.0

Use that value in the archive name you send back.

Step 5: Create A Clean Virtual Environment

From the repository root:

python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip setuptools wheel

Step 6: Install Build Dependencies

Install the project requirements and PyInstaller:

python -m pip install -r requirements.txt
python -m pip install pyinstaller

If wxPython fails to install, stop there and report the exact pip error. Beam cannot be packaged correctly without it.

Step 7: Verify The Python Architecture

Before building, verify that the active Python supports the target you want:

python - <<'PY'
import platform
print(platform.platform())
print(platform.machine())
PY

file "$(python -c 'import sys; print(sys.executable)')"

For a single universal build, the Python installation itself needs universal2 support.

Step 8: Build The macOS App Bundle

This is the standard Beam macOS build command:

VERSION_TAG="$(python - <<'PY'
import json
from pathlib import Path
version = json.loads(Path('resources/json/strings.json').read_text(encoding='utf-8'))['version']
print('v' + version.removesuffix('.0'))
PY
)"

python -m PyInstaller \
  --noconfirm \
  --clean \
  --windowed \
  --target-arch universal2 \
  --name "Beam" \
  --icon "resources/icons/installer_icon/icon_MacOS.icns" \
  --osx-bundle-identifier "com.beamproject.beam" \
  --add-data "resources:resources" \
  --add-data "docs:docs" \
  beam.py

Expected output:

dist/Beam.app

Step 9: Verify The Built App

Run these checks before zipping:

open dist/Beam.app

Confirm:

Then verify the app binary architecture:

file "dist/Beam.app/Contents/MacOS/Beam"
lipo -info "dist/Beam.app/Contents/MacOS/Beam"

For a universal build, lipo -info should report both arm64 and x86_64.

Step 10: Zip The App Bundle

Use ditto, not Finder compression and not plain zip -r.

mkdir -p release-artifacts

ARCHIVE_NAME="beam-mac-${VERSION_TAG}-universal2.zip"

ditto -c -k --sequesterRsrc --keepParent \
  "dist/Beam.app" \
  "release-artifacts/${ARCHIVE_NAME}"

Expected output:

release-artifacts/beam-mac-v0.9.1.0-universal2.zip

Step 11: Sanity-Test The Zip

Before sending the file back:

rm -rf /tmp/Beam-smoke
mkdir -p /tmp/Beam-smoke
ditto -x -k "release-artifacts/${ARCHIVE_NAME}" /tmp/Beam-smoke
open /tmp/Beam-smoke/Beam.app

This confirms the archive expands correctly and still launches.

Step 12: Send Back These Files

The collaborator should send back:

Recommended handoff note:

Built Beam from commit <sha> on macOS <version> using Python 3.12.x from python.org.
Artifact: beam-mac-v<version>-universal2.zip
PyInstaller target: universal2
Smoke check: app opened and main window appeared

Fallback: Build Separate Apple Silicon And Intel Archives

If --target-arch universal2 fails because the local Python or one of the binary wheels is not universal-compatible, build separate archives instead.

Apple Silicon build

On an Apple Silicon Mac using native Terminal:

python -m PyInstaller \
  --noconfirm \
  --clean \
  --windowed \
  --target-arch arm64 \
  --name "Beam" \
  --icon "resources/icons/installer_icon/icon_MacOS.icns" \
  --osx-bundle-identifier "com.beamproject.beam" \
  --add-data "resources:resources" \
  --add-data "docs:docs" \
  beam.py

Zip it as:

ditto -c -k --sequesterRsrc --keepParent \
  "dist/Beam.app" \
  "release-artifacts/beam-mac-${VERSION_TAG}-arm64.zip"

Intel build

Preferred option:

Fallback option on Apple Silicon:

Command:

python -m PyInstaller \
  --noconfirm \
  --clean \
  --windowed \
  --target-arch x86_64 \
  --name "Beam" \
  --icon "resources/icons/installer_icon/icon_MacOS.icns" \
  --osx-bundle-identifier "com.beamproject.beam" \
  --add-data "resources:resources" \
  --add-data "docs:docs" \
  beam.py

Zip it as:

ditto -c -k --sequesterRsrc --keepParent \
  "dist/Beam.app" \
  "release-artifacts/beam-mac-${VERSION_TAG}-x86_64.zip"

Troubleshooting

wxPython does not install

PyInstaller build succeeds but the app crashes on launch

dist/Beam.app/Contents/MacOS/Beam

Universal build does not contain both architectures

Notes