Development =========== Build against onnx-light main ----------------------------- The default CMake configuration fetches onnx-light's ``main`` branch. Python bindings are disabled for native-only builds; Python test options automatically enable the matching onnx-light model bindings. Upstream tests, benchmarks and built-in operator kernels remain disabled. The outgoing common library compiles only the existing LeakyRelu kernel source alongside the core; unused targets are excluded from the default build. Plugin builds additionally fetch onnx-light-cpu at ``054cdd984d9f85a7692dc4f678b2f13275e8ede1`` and configure only its standalone ``ml_cpu_compute`` subproject. No onnx-light-cpu Python package or additional onnx-light build is needed. Its runtime SIMD dispatch remains enabled; do not add target-wide native/AVX compiler flags. To supply that same source checkout, set ``FETCHCONTENT_SOURCE_DIR_ONNX_LIGHT_CPU=/path/to/onnx-light-cpu``. Dependency headers are treated as system headers; warnings remain errors in this project's own sources. Set ``Python3_EXECUTABLE`` to select an interpreter explicitly. The same interpreter is forwarded to the onnx-light bindings build. .. code-block:: bash cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake --build build --parallel "$(nproc)" ctest --test-dir build --output-on-failure On Windows or other multi-configuration generators, also pass ``--config Release`` to the build and ``-C Release`` to CTest. On macOS use ``sysctl -n hw.logicalcpu`` in place of ``nproc``. Keep one build tree; reconfigure it rather than maintaining stale parallel compiled copies. Build either direction independently: .. code-block:: bash cmake -S . -B build -DONNX_LIGHT_ORT_BUILD_HOST=ON \ -DONNX_LIGHT_ORT_BUILD_PLUGIN=OFF cmake --build build --parallel "$(nproc)" ctest --test-dir build --output-on-failure Reverse the two options for a plugin-only build; both are enabled by default. The plugin option selects ``custom_ops``, both ``*_light_to_ort`` bridges and ``common_light_to_ort``. The host option selects both ``*_ort_to_light`` bridges and ``common_ort_to_light``. Unimplemented EP bridges remain interface targets; they do not acquire fake runtime implementations just by being enabled. The EP entry-point library will have its own target when implemented. ``ONNX_LIGHT_ORT_BUILD_TESTS=OFF`` disables native integration tests. When switching to a host-only build, also turn ``ONNX_LIGHT_ORT_TEST_WITH_ORT=OFF`` if previously enabled. To use an existing checkout explicitly: .. code-block:: bash git -C /path/to/onnx-light fetch origin main cmake -S . -B build \ -DFETCHCONTENT_SOURCE_DIR_ONNX_LIGHT=/path/to/onnx-light The override uses the checkout exactly as supplied and does not switch branches. Ensure it contains the desired main revision; CMake logs its HEAD. CI explicitly checks out upstream ``main`` and passes this override so its resolved commit appears in the job logs. Fresh CI runs and the daily schedule exercise new upstream main changes without a release. Component tests and examples ----------------------------- Each implemented component owns its native tests. Select CTest labels ``custom_ops``, ``bridge_ops_light_called_by_ort``, ``common_light_to_ort`` or ``common_ort_to_light`` or ``bridge_ops_ort_called_by_light`` to run a component's tests. The ``unit`` label selects native unit suites. Outgoing units use a minimal explicit C API host, not a mocking framework or an ORT installation. Incoming native tests load an independent ORT ``LeakyRelu`` custom-op library into the real standalone host; the dependency-contract smoke test is retained. EP bridges and the EP entry-point library have no executable tests. See :doc:`components/index` for each component's page and precise coverage. The C++ custom-op example is built by default when plugins are enabled, without linking ORT. ``ONNX_LIGHT_ORT_BUILD_EXAMPLES=OFF`` disables it. Both :doc:`components/custom_ops` examples run inference in a real ORT host. To additionally execute the C++ example through CTest, pass an existing runtime: .. code-block:: bash cmake -S . -B build -DONNX_LIGHT_ORT_TEST_WITH_ORT=ON \ -DONNX_LIGHT_ORT_RUNTIME_LIBRARY=/absolute/path/to/libonnxruntime.so cmake --build build --parallel "$(nproc)" ctest --test-dir build -L example --output-on-failure Test in a real ORT host ----------------------- The native build never needs an ORT runtime. Enable the separate integration tests explicitly with Python 3.12 or later: .. code-block:: bash python -m pip install -r requirements/ort-tests.txt cmake -S . -B build -DONNX_LIGHT_ORT_BUILD_PLUGIN=ON \ -DONNX_LIGHT_ORT_BUILD_TESTS=ON -DONNX_LIGHT_ORT_TEST_WITH_ORT=ON cmake --build build --parallel "$(nproc)" ctest --test-dir build --output-on-failure The tests build models with custom domain ``onnx_light``, opset 1, operator ``LeakyRelu`` and an explicit float ``alpha``. They load the produced shared library with ``SessionOptions.register_custom_ops_library`` and verify repeated dynamic/scalar/empty shapes, read-only input preservation, preallocated output buffers, independent sessions, and invalid attributes, arity, types, rank and alignment or overlapping buffers. Expected values come from NumPy, not a substitute native kernel. They also execute ``onnx_light_cpu::Log`` and check its special values and SIMD tails against a NumPy float64 reference, rounded to float32, with relative tolerance ``2e-6`` and zero absolute tolerance. Both operators also run in float16, bfloat16 and float64 through real ORT and the standalone host, preserving dtype. The float64 cases include values that would lose precision or overflow if routed through float32. On Linux the loadable library is ``build/onnx_light_ort/custom_ops/libonnx_light_ort_custom_ops.so``. CTest resolves the platform-specific path from the custom-op target, including multi-configuration build directories. The real-host CI job uses ORT 1.18.1 with NumPy below 2 to exercise the requested API-18 baseline. Models are created and checked with ``onnx_light.onnx``, never the ``onnx`` Python package. Documentation and style ----------------------- Sphinx builds the matching onnx-light Python bindings and native gallery libraries in the existing ``build`` tree, then executes both gallery examples. The standard command below also works from a fresh checkout without setting ``PYTHONPATH`` or library environment variables. It requires Python 3.12 or later, a C++20 compiler, Git and network access to fetch the native dependencies. No local project artifacts are installed into site-packages. .. code-block:: bash python -m pip install -r requirements/style.txt -r docs/requirements.txt black . && ruff check . git ls-files -z '*.cc' '*.cpp' '*.h' '*.hpp' | xargs -0 clang-format -i git ls-files -z '*.cc' '*.cpp' '*.h' '*.hpp' | xargs -0 clang-format --dry-run --Werror python -m sphinx -b html -W --keep-going docs build/docs The build and style tools ignore downloaded dependencies and generated output. Native tests use the real onnx-light core without mocking libraries or an installed Python package. Reverse-direction units use explicit test-owned opaque values and API callbacks. Nothing is installed into site-packages by the native build. Standalone Python custom-op tests --------------------------------- Use the source checkout through ``PYTHONPATH``; do not install the project. The incoming host uses NumPy and the onnx-light Python model bindings, not the ``onnx`` package or an ONNX Runtime installation: .. code-block:: bash python -m pip install -r requirements/host-tests.txt cmake -S . -B build -DONNX_LIGHT_ORT_BUILD_HOST=ON \ -DONNX_LIGHT_ORT_BUILD_PLUGIN=OFF -DONNX_LIGHT_ORT_TEST_WITH_ORT=OFF \ -DONNX_LIGHT_ORT_TEST_PYTHON=ON cmake --build build --parallel "$(nproc)" ctest --test-dir build -L bridge_ops_ort_called_by_light --output-on-failure The build places the onnx-light model package under ``build/python`` and CTest adds it to ``PYTHONPATH``. It uses the same source revision as the native host; no project artifacts are installed into site-packages. For direct Python use, set ``PYTHONPATH="$PWD:$PWD/build/python:$PYTHONPATH"``. On Windows, CTest additionally registers the native test ``PATH`` directories with ``os.add_dll_directory`` for the lifetime of each Python script. Python 3.8 and later do not search ``PATH`` when loading extension-module dependencies. This includes external DLLs needed by the onnx-light libraries, not just the libraries placed under ``build/python``. Direct Windows invocations can use ``python cmake/run_python_with_dll_paths.py path/to/script.py ...`` as well. ``CustomOpSession`` accepts an onnx-light ``ModelProto`` or serialized model bytes and explicit plugin/host library paths. Its audited single-node floating-point profile is documented in :doc:`components/bridge_ops_ort_called_by_light`; it is not a general plugin host. Executable Python gallery -------------------------- The :doc:`auto_examples/index` gallery runs both custom-op directions. Sphinx prepares its prerequisites automatically. To also run the integration tests or execute the scripts directly on Linux, build both sides and set the three library paths: .. code-block:: bash python -m pip install -r docs/requirements.txt cmake -S . -B build -DONNX_LIGHT_ORT_BUILD_HOST=ON \ -DONNX_LIGHT_ORT_BUILD_PLUGIN=ON -DONNX_LIGHT_ORT_TEST_PYTHON=ON \ -DONNX_LIGHT_ORT_TEST_WITH_ORT=ON cmake --build build --parallel "$(nproc)" ctest --test-dir build --output-on-failure export PYTHONPATH="$PWD:$PWD/build/python:$PYTHONPATH" export ONNX_LIGHT_ORT_PLUGIN_LIBRARY="$PWD/build/onnx_light_ort/custom_ops/libonnx_light_ort_custom_ops.so" export ONNX_LIGHT_ORT_HOST_LIBRARY="$PWD/build/onnx_light_ort/bridge_ops_ort_called_by_light/libonnx_light_ort_bridge_ops_ort_called_by_light.so" export ONNX_LIGHT_ORT_INCOMING_CUSTOM_OP_LIBRARY="$PWD/build/onnx_light_ort/bridge_ops_ort_called_by_light/libonnx_light_ort_ort_custom_ops.so" python -m sphinx -b html -W --keep-going docs build/docs On macOS use the corresponding ``.dylib`` paths; on Windows use the ``.dll`` paths under the selected build configuration. CTest resolves these paths automatically. The ``gallery_custom_ops_light_called_by_ort`` and ``gallery_custom_ops_ort_called_by_light`` tests execute the same gallery scripts in separate Python processes. The incoming example never imports ORT; the outgoing example uses a real ORT session. No EP is involved, and failures are not skipped or hidden. Sphinx builds only the model bindings and the three gallery libraries, not the native tests. Existing CMake dependency-source overrides are preserved, and explicitly supplied gallery library environment variables take precedence. The ``gallery_benchmark_log_custom_ops`` test executes the Log benchmark gallery. It compares NumPy, all four synchronous ORT calling interfaces with built-in and CPU custom operators, and the standalone single-node host. It uses one Log or ten consecutive Logs, checks results before and after timing, and reports median microseconds per graph over several input sizes. Ten Logs produce NaNs in float32; this measures the literal chain, including special values. The standalone host cannot execute that topology and is marked unavailable. Measurements run in separate processes to keep ORT out of the standalone host. Use the same library environment variables shown above. All benchmark code lives in ``docs/examples/plot_benchmark_log_custom_ops.py``, including model construction, timing, isolated workers and the CLI entry point. The command-line benchmark also accepts ``--nodes 1|10`` and ``--api run|ortvalues|iobinding|ortvaluevector``. CI and documentation publishing ------------------------------- Native CI covers Linux, Windows and macOS, including host-only, plugin-only and combined configurations. It uses a compiler cache, cancels superseded branch runs, and builds against upstream main. A Linux check rejects an ORT shared-library dependency in the native test executable and plugin library. Real-host tests run in a separate Linux job; host-only jobs do not install ORT. Release publication is handled separately by ``.github/workflows/releases.yml``; ordinary native CI does not create releases. Tagged releases --------------- The ``Releases`` workflow starts automatically when a tag is pushed. For version 0.2.0, both ``0.2.0`` and ``v0.2.0`` are accepted. It checks that the tag, CMake project, Python metadata and ``__version__`` agree before building. It resolves onnx-light's main revision once and uses that immutable commit in every release job; onnx-light-cpu remains independently pinned. Each package includes ``release-build-info.txt`` and the dependency licenses. The targets match onnx-light's release matrix: .. list-table:: :header-rows: 1 * - Platform - Architectures - CPython wheels * - Linux, manylinux 2.28 - x86_64, aarch64 - 3.12, 3.13, 3.14 * - Windows - AMD64, ARM64 - 3.12, 3.13, 3.14 * - macOS 13.3+ - universal2 (arm64 + x86_64) - 3.12, 3.13, 3.14 There are fifteen wheels and five native C++ archives. Native archives use Ubuntu 24.04 on Linux; they are not the portable manylinux wheels. Windows archives are ZIP files; Linux and macOS archives are gzip-compressed tar files. All builds enable both custom-op directions and use all available processors. No ORT runtime or ``onnx`` Python package is bundled. Native archive builds disable Python and optional OpenSSL support. They contain the two shared libraries with statically linked onnx-light/CPU dependencies, public host and ORT API headers, and a relocatable CMake package. Each job moves the installation and builds/runs an independent downstream consumer: .. code-block:: cmake find_package(onnx_light_ort 0.2 CONFIG REQUIRED) target_link_libraries(my_host PRIVATE onnx_light_ort::bridge_ops_ort_called_by_light) ``onnx_light_ort::custom_ops`` is the installed plugin target. Wheels bundle the host, plugin and their native runtime dependencies under ``onnx_light_ort/_native``. Bundled dependency libraries use private names to avoid ABI collisions with a separately installed onnx-light model package. The matching onnx-light model bindings are built in the same tree for wheel smoke tests, but are not copied into this package. The tests load the installed libraries first and execute LeakyRelu and CPU Log without ORT. Dependency repair uses auditwheel, delvewheel or delocate as appropriate; Windows repair preserves already bundled DLLs. Success on one platform does not publish a partial release: publication waits for every wheel and native job, then attaches all artifacts and ``SHA256SUMS``. A retry can replace artifacts on the same release. ``workflow_dispatch`` without a tag builds and tests artifacts without publishing. With an existing tag it can retry a release. This workflow publishes to GitHub Releases, not PyPI; pushing a tag is an explicit maintainer action. To build a wheel locally without installing it, use the existing build tree. Set ``CMAKE_GENERATOR`` to its current generator (``Unix Makefiles`` in this Linux example), rather than creating another compiled tree: .. code-block:: bash CMAKE_GENERATOR="Unix Makefiles" CMAKE_BUILD_PARALLEL_LEVEL="$(nproc)" \ python -m pip wheel . --no-deps -w build/wheelhouse For source-checkout work, continue using ``PYTHONPATH`` rather than installing local artifacts. Wheel builds reconfigure ``build``; restore the desired native options and ``-DSKBUILD=OFF`` before returning to a plain CMake development build. Daily coverage --------------- The ``Coverage`` workflow runs every day at 07:00 UTC on ``main`` and can also be started manually. It builds both native directions with GCC coverage instrumentation and runs all native CTest tests. Coverage includes the C++ implementation and headers under ``onnx_light_ort/``, excluding test sources, downloaded dependencies, generated files and examples. Python and opt-in real-ORT tests are not included. The workflow uploads the Cobertura XML report to `Codecov `_ using GitHub OIDC. Enable this repository in Codecov before the first upload; no ``CODECOV_TOKEN`` secret is required. Upload failures fail the workflow. The README and documentation badges show the latest ``main`` coverage. HTML and XML reports are also retained as the ``coverage`` Actions artifact for 14 days. Relevant pull requests generate these artifacts without publishing to Codecov, so they do not replace the daily ``main`` report. To reproduce the native report locally, reconfigure the existing build tree: .. code-block:: bash python -m pip install -r requirements/coverage.txt CC=gcc CXX=g++ cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CXX_FLAGS="--coverage -O0 -g" \ -DCMAKE_EXE_LINKER_FLAGS="--coverage" \ -DCMAKE_SHARED_LINKER_FLAGS="--coverage" \ -DONNX_LIGHT_ORT_BUILD_HOST=ON -DONNX_LIGHT_ORT_BUILD_PLUGIN=ON \ -DONNX_LIGHT_ORT_BUILD_TESTS=ON -DONNX_LIGHT_ORT_TEST_WITH_ORT=OFF cmake --build build --parallel "$(nproc)" ctest --test-dir build --output-on-failure mkdir -p build/coverage python -m gcovr build --root . \ --filter 'onnx_light_ort/' --exclude '.*/tests/.*' \ --xml-pretty --xml build/coverage/coverage.xml \ --html-details build/coverage/index.html --print-summary Fuzzing onnx-light ------------------ The optional fuzz targets are provided by the fetched onnx-light dependency and require Clang with libFuzzer support: .. code-block:: bash CC=clang CXX=clang++ cmake -S . -B build-fuzz \ -DONNX_LIGHT_ORT_BUILD_FUZZERS=ON \ -DONNX_LIGHT_ORT_BUILD_TESTS=OFF cmake --build build-fuzz --parallel "$(nproc)" ./build-fuzz/_deps/onnx_light-build/fuzz_checker -runs=2000 Fuzzing is disabled by default. The ``fuzz`` GitHub Actions workflow builds the available upstream harnesses and runs short smoke campaigns; it does not replace longer coverage-guided fuzzing. Documentation builds on pull requests, main pushes and manual dispatch. Only successful main-branch documentation builds deploy to GitHub Pages. Workflow tokens are read-only by default. Pages write permission is restricted to documentation deployment; OIDC write permission is granted to documentation deployment and the coverage job for Codecov authentication.