Files
ocp/docs/building.md
stroblme 8175b8aff3 docs
Signed-off-by: stroblme <stroblme@posteo.de>
2026-08-11 15:07:47 +02:00

4.1 KiB

Building

Everything runs through makemake help lists the targets. All compilation happens inside the OCCT builder image, so the only things you need on the host are Docker and, for publishing, uv.

The builder image

occt/Dockerfile compiles OCCT 8.0.1 once inside quay.io/pypa/manylinux_2_28_x86_64 (both the base digest and the source tarball's sha256 are pinned) and installs it to /opt/occt.

make image
make image-push

It's a compiler appliance: wheel builds mount the repo into it rather than building FROM it, so iterating on the binding never re-layers the kernel. Add new packages at the end of the Dockerfile — earlier layers stay cached and OCCT doesn't recompile.

The configuration turns Draw, VTK, Tk, Xlib, OpenGL and GLES off and FreeType on, and the last build layer checks the result: TKService and TKV3d exist (needed for text emboss via Font_BRepFont), neither links libGL/libX11, and FreeType is linked. Flags are -O2, no -ffast-math, no -march=native — the build must produce the same floating-point results as any other build of the same kernel version, since downstream code compares geometry output across builds.

Build cache

Object files, ccache, the pip cache and the build venv live under $(CACHE), default /mnt/cache/n3xd/ocp — kept off the root filesystem, which is tight on the dev box. Override it per invocation with make CACHE=/somewhere wheel, or reset it with make clean-cache.

The loop

make dev       # incremental compile + pytest — the inner loop, seconds
make test-asan # handle-model memory-safety check
make wheel     # full build: compile, stubs, repack, auditwheel, smoke test

make wheel compiles twice on purpose: stubs come from importing the freshly built extension, so they can't exist before the first compile, and the wheel is packed from the source tree afterwards. The second pass is incremental.

The smoke test installs the repaired wheel into a bare venv and imports it with LD_LIBRARY_PATH unset — the only real proof that auditwheel made it self-contained.

Fixtures

tests/data/*.brep are byte-identity references, generated under the stock wheel from a checkout of the app that consumes this binding:

cd ../app && .venv/bin/python ../ocp/tools/gen_fixtures.py

The generator asserts each fixture is idempotent under stock before recording its digest, so the gate compares against a fixed target rather than a moving one. Regenerate only when deliberately re-blessing — a kernel bump doesn't automatically mean the fixtures need it, so run the gate first and let it tell you before touching tests/data/. The generator also needs a wheel carrying more of OCCT than this binding exposes, so it can't currently run from this project's own venv.

Publishing

make version   # confirm what you're about to publish
make publish   # uv publish -> https://git.stroblme.de/api/packages/N3XD/pypi

Credentials come from .secrets (gitignored) as UV_PUBLISH_USERNAME / UV_PUBLISH_PASSWORD — a real Gitea username rather than PyPI's __token__ convention, with a token scoped to package: Read and Write.

A version can never be republished. Iteration builds carry a .devN suffix and are the only ones the registry's cleanup rule collects — bump N in pyproject.toml for each upload.

Consumers install anonymously, since the registry is public:

uv pip install --index-url https://git.stroblme.de/api/packages/N3XD/pypi/simple/ \
    --prerelease=allow n3xd-ocp

Gitea serves no root /simple/ listing (404), only the per-package path, which is all pip and uv ever ask for.

Parity

tools/parity_venv.sh builds a side environment where the app runs against this wheel instead of the stock one, without touching the app's own manifests — both distributions own the OCP/ import path, so swapping is per-environment and reversible by re-syncing.

tools/parity_venv.sh           # from the registry
tools/parity_venv.sh --local   # from wheelhouse/
python tools/inventory.py --check

Coverage is expected to be partial until everything is bound — --check prints what's still missing, grouped by module.