Skip to content

Latest commit

 

History

History
186 lines (147 loc) · 8.26 KB

File metadata and controls

186 lines (147 loc) · 8.26 KB
name ifcopenshell-docker-build
description Build a real ifcopenshell_wrapper (.so + .py) and IfcConvert locally via the docker/ifcos_env toolchain, then wire them into a checkout for running C++-dependent parts of the test suite (geometry, the SWIG wrapper stub, the C++ parser). Use whenever a task needs to compile IfcOpenShell's C++ core rather than just read/patch source - e.g. reproducing or fixing a bug in src/ifcgeom, src/ifcparse, src/ifcwrap, or validating util/scripts/validate_stub.py against the actual generated wrapper.

Building IfcOpenShell locally with docker/ifcos_env

docker/ mirrors the project's GitHub Actions build environment locally, in a persistent, non-root container with ccache so repeat builds are fast. See docker/README.md for the design rationale. Pure-Python changes don't need any of this - only reach for it when you need a real compiled _ifcopenshell_wrapper*.so or IfcConvert binary.

Placement

This docker/ folder must live as a direct child of the repo root you want to build (sibling of src/, cmake/, etc.) - compose.yaml and ifcos_env resolve the repo via ../ relative to wherever docker/ itself sits, and bind-mount it into the container. If you're setting this up in a fresh clone, copy the whole docker/ directory there first.

Setup

cd docker
./ifcos_env create   # build the image (shared by name across all your clones/checkouts, so usually instant after the first time anywhere)
./ifcos_env up        # create + start the container, clone/unpack the third-party dependency cache (~10GB, one-time per container)
./ifcos_env build     # full build: all deps + IfcParse + IfcGeom + IfcConvert + the Python wrapper, for one Python version

PY_TGT and UNIQUE_ID live in docker/.env - PY_TGT (e.g. py-311) restricts the build to one Python version instead of building five; UNIQUE_ID is a hash of the folder path, recalculated on every up, so each checkout gets its own container/volumes automatically.

A full first build takes ~1.5 hours (mostly compiling IfcOpenShell's own C++, not the cached third-party deps). After that, ccache makes incremental rebuilds of a couple of touched .cpp files under a minute.

Container lifecycle

The container is long-lived (sleep infinity) so exec'd commands and ccache state persist between builds. Commands map directly onto Docker Compose's own container-vs-image distinction:

./ifcos_env up         # create the container if it doesn't exist, then start it (runs ready_repo too)
./ifcos_env stop       # stop the container, keep it around
./ifcos_env start      # start it back up (same container, same filesystem layer)
./ifcos_env restart    # stop, then start
./ifcos_env down       # remove the container (and its network) entirely
./ifcos_env recreate   # down, then up - a fresh container

Named volumes (ccache) and the bind-mounted repo/build/ are unaffected by down/recreate - only the container itself goes away, and up recreates it from the image.

Fast iteration

Pass a target to build to skip the parts you don't need:

./ifcos_env build IfcConvert           # only the executables (IfcConvert, IfcGeomServer) - skips the Python wrapper entirely
./ifcos_env build IfcOpenShell-Python  # only the SWIG Python wrapper - skips executables entirely
./ifcos_env build                      # no target = everything (needed the first time, or after touching shared headers)

Use this to keep the edit -> rebuild -> test loop fast when debugging: if you're only touching src/ifcgeom/, build IfcConvert; if you're only exercising the Python API, build IfcOpenShell-Python.

Where the artifacts land

Build output goes to <repo_root>/build/Linux/x86_64/install/ on the host (bind-mounted, not just inside the container), owned by you (see "Container user" below):

  • ifcopenshell/bin/IfcConvert - the CLI binary
  • python-<version>/lib/python<X.Y>/site-packages/ifcopenshell/_ifcopenshell_wrapper*.so and ifcopenshell_wrapper.py - the compiled wrapper + its generated Python glue

Testing against a checkout (automated / AI-driven)

_ifcopenshell_wrapper*.so and ifcopenshell_wrapper.py are already gitignored under src/ifcopenshell-python/ifcopenshell/, which is exactly where a normal in-tree build would put them - copy the two files there:

SRC=build/Linux/x86_64/install/python-3.11.8/lib/python3.11/site-packages/ifcopenshell
cp "$SRC/_ifcopenshell_wrapper.cpython-311-x86_64-linux-gnu.so" src/ifcopenshell-python/ifcopenshell/
cp "$SRC/ifcopenshell_wrapper.py" src/ifcopenshell-python/ifcopenshell/

Then, to run the test suite against it:

export PATH="$PWD/build/Linux/x86_64/install/ifcopenshell/bin:$PATH"   # for IfcConvert-dependent tests
cd src/ifcopenshell-python/test
PYTHONPATH="$PWD/.." python3.11 -m pytest -p no:pytest-blender .

(-p no:pytest-blender avoids the pytest-blender plugin trying to find a blender executable and failing collection entirely, even for non-Blender tests.) You'll need the matching Python version's pip installs too (numpy, shapely, isodate, lark, tabulate, pytest, ... - whatever the modules under test import) since this is a bare interpreter, not the project's pixi env.

This is the pattern to use for automated or AI-driven verification. Don't use try (below) for that - it overwrites files in a real, live Blender installation, which isn't something an automated/AI workflow should ever do without the human explicitly asking for it in the moment.

Testing in Blender itself (human only)

try copies the built wrapper straight into your actual Blender/Bonsai extension install, for manual in-Blender testing:

./ifcos_env try

It reads BLENDER_USER_RESOURCE from .env - set this to wherever Blender's user resource folder for the Bonsai extension actually lives on your system, which depends on your own Blender setup:

# in docker/.env
BLENDER_USER_RESOURCE=~/.config/blender/bonsai/

try figures out the built Python version from build/.../install/ (disambiguating with PY_TGT if more than one version was built) and copies the wrapper to $BLENDER_USER_RESOURCE/extensions/.local/lib/python<X.Y>/site-packages/ifcopenshell/.

Container user

The image runs as a non-root builder user, UID/GID matching your host account (passed as --build-arg by create from id -u/id -g, so it adjusts automatically - no manual flag needed even if you're not 1000:1000). Files the build creates under the bind mount come out owned by you, not root. Passwordless sudo is available inside the container (e.g. via attach) for the rare case you need root for something ad hoc.

If you're picking up an existing checkout that was previously built with an older, root-based image, you may hit Permission denied the first time you run up/build under the new image - build/, .git/modules/, the ccache volume, output/, and build.log can all be left root-owned from before. Fix it once via the container's own root (no host sudo needed):

docker exec -u root -w /__w/IfcOpenShell/IfcOpenShell <container-name> \
    chown -R "$(id -u)":"$(id -g)" .git/modules build output build.log /ccache

(<container-name> is ifcopenshell-<UNIQUE_ID> - see docker ps -a.)

Other things worth knowing

  • Linux x64 only. compose.yaml pins platform: linux/amd64; on an ARM host (e.g. Apple Silicon) this build isn't available.
  • The final "Package .zip archives" step of build() has a pre-existing bash syntax error, unrelated to compilation - the actual build already succeeded by that point (look for Built IfcOpenShell... in the output), so this is safe to ignore if you only need the raw artifacts under build/.../install/, not packaged release zips.
  • test_mmaped_stream and similar USE_MMAP-dependent tests will fail against this build - nix/build-all.py is invoked with USE_MMAP=OFF here. Not a bug in your code if you see it fail.
  • Only the bind-mounted <repo>/build lives on the host filesystem your repo is checked out on. Anything the container writes outside that mount lives in the container's own writable layer under Docker's data root (commonly /var/lib/docker, i.e. usually your root partition) - keep an eye on df -h / if you're running several of these containers at once.