Skip to content

Build and Test

This project uses a Python build backend that configures and builds the C++ core with CMake.

Local setup

Activate the Python environment you use for development before running import checks or tests. Editable installs should use the normal rebuild behavior so native C++ changes are reflected in Python runtime checks.

python -m pip install --upgrade pip
python -m pip install -e .[dev]

If your environment does not provide extras, install the minimal tools directly:

MIFROST_BUILD_BACKENDS=pymimir python -m pip install -e .[pymimir]
python -m pip install pytest mkdocs mkdocs-material

Build and install

Use a named build mode for each local purpose. configure.py and cbuild.py read these modes from local_build_dirs.py, so the default directories, configs, benchmark toggle, and default build targets stay in one place:

  • local-release: build/local-release, Release, target all
  • local-debug: build/local-debug, Debug, target all
  • stubs: build/stubs, Release, target mifrost_core_module_stubs
  • ci: build/ci, Release, target mifrost_tests
  • bench: build/bench-release, Release, benchmarks enabled, target mifrost_bench_hgraph

Avoid ad hoc root-level build directories like build_*_probe unless they are short-lived ignored experiments.

Dev package staging

A dev-mode build stages an importable mifrost package: the native extension modules, the shared libraries they load at runtime, and the generated stubs. MIFROST_DEV_PACKAGE_ROOT controls where that package is written.

  • Plain CMake builds stage into <build-dir>/python, so each configuration keeps its artifacts to itself.
  • The scikit-build-core editable install and --mode stubs stage into src/, because an editable install imports from the source tree and packaging reads the generated src/mifrost/*.pyi files afterwards.

Sharing one staging root between build directories with different compile settings is what makes a sanitizer or Debug build replace the shared library that another build directory's already linked tests and benchmarks load. The symptom is not a build error; it is spurious sanitizer reports, slow benchmarks, or an aborted stub generation in the other build tree. Configure rejects that overlap and names both build directories. Pass -DMIFROST_DEV_PACKAGE_ROOT=<dir> to move a build elsewhere.

If you do build a sanitizer or Debug tree that stages into src/, rebuild the editable install afterwards so the source tree holds Release artifacts again.

The Python helpers treat those modes as the canonical local build roots:

  • configure.py prepares a build tree and installs dependencies.
  • cbuild.py only builds an already configured tree.
  • build_backend.py owns editable/wheel build-time environment setup.

You can still pass --build_dir, --config, or --target for short-lived experiments, but prefer --mode for checked-in commands and reproducible developer workflows.

Install the package (this triggers a full native build):

python -m pip install -v ".[backends]"

For editable development:

python -m pip install -e ".[backends]"

The Python build backend builds both native adapters by default. Set MIFROST_BUILD_BACKENDS to core, pymimir, pytyr, or both to exercise a specific installation variant. This controls native targets; the matching project extra controls runtime planner dependencies.

The CMake helper exposes the same selection directly:

python configure.py --mode local-release --backends pytyr

For explicit CMake-driven local builds, the blessed layout is:

python configure.py --mode local-release
python cbuild.py --mode local-release

Use the debug mode when you need a debug tree:

python configure.py --mode local-debug
python cbuild.py --mode local-debug

Generated stubs

src/mifrost/_core.pyi is generated by nanobind, not hand-edited. The file is ignored in version control, but the packaging flow still expects it to exist:

  • CI generates it before the source distribution and wheel jobs run.
  • pyproject.toml includes it in the source distribution, so the wheel build path can package the generated stub.
  • Local editable or stub-oriented work should regenerate it from the CMake target rather than editing the file directly.

Local generation commands:

python configure.py --mode stubs
python cbuild.py --mode stubs
python scripts/validate_generated_stub.py

The validation script is the same guard used by CI. It confirms that the stub file was produced, is non-empty, and is valid Python syntax.

Run tests

Run the full Python test suite:

python -m pytest

Run a single test module while iterating:

python -m pytest tests/encoding/test_color_encoder.py -q

Run the C++ test target through the CI-like mode:

python configure.py --mode ci
python cbuild.py --mode ci
ctest --test-dir build/ci -R mifrost_tests --output-on-failure

Build the benchmark target:

python configure.py --mode bench
python cbuild.py --mode bench

Build docs

Strict docs build (fails on warnings):

mkdocs build --strict