2.4.3. Packaging a Simulator as a Component Repository#
In the modular SimBricks layout, every simulator integration lives in its own component
repository under the SimBricks GitHub organization (component-<x>) and is distributed as a
set of conda packages through the SimBricks conda channel. This page describes the pattern, so
you can follow it for your own simulator — whether you publish it publicly or keep it internal.
Tip
The best reference implementations are component-corundum (RTL simulator via Verilator, including a guest driver), component-qemu (large external simulator integrated via a fork + submodule), and component-i40e (self-contained behavioral model). Have a look at their READMEs and copy liberally.
2.4.3.1. Repository layout#
Component repositories all follow the same skeleton:
component-<x>/
.github/workflows/conda-packages.yml # thin caller of the shared CI workflow
conda-recipes/
conda_build_config.yaml # version pin for all packages in this repo
simbricks-<x>-...-bin/ # recipe for the binary package
build.sh
meta.yaml
simbricks-<x>-...-py/meta.yaml # recipe(s) for the Python package(s)
<src>/ # simulator/adapter sources, or a git submodule
<x>_sys_py/simbricks/components/<x>/system/... # optional system components
<x>_sim_py/simbricks/components/<x>/simulation/... # simulator class(es)
Makefile # local dev build + conda-packages + pypi targets
LICENSE, README.md
For simulators with a large existing code base (QEMU, gem5, ns-3, FEMU), the simulator itself is a git submodule pointing to a SimBricks fork that carries the Adapter (e.g. simbricks/qemu, simbricks/gem5, simbricks/ns-3). For self-contained models (i40e, e1000, the basic net/mem simulators), the sources live directly in the repository.
2.4.3.2. The package triple#
A full integration produces up to three kinds of packages (see also Component Packages: simbricks.components.*):
simbricks-<x>-sys-py— system components (noarch Python). Depends only onsimbricks-orchestration. Only needed when the component introduces new system classes (e.g. a NIC model plus a host class that loads its driver). Simulators that only simulate existing generic components (e.g. QEMU simulating a generic Linux host) don’t need one.simbricks-<x>-sim[-<flavor>]-py— the simulator class (noarch Python). Depends onsimbricks-orchestration(and, if present, pins the co-built-sys-pypackage with==). The flavor distinguishes multiple simulators for the same component:bmfor a behavioral model,rtlfor RTL simulation via Verilator.simbricks-<x>-sim[-<flavor>]-bin— the compiled simulator binary (linux-64). Built against thesimbricks-libpackage (the SimBricks protocol libraries and headers) and installed into$PREFIX/bin. Crucially, itsrun:dependencies pin the sibling Python package(s) with==— somicromamba install simbricks-corundum-sim-rtl-binpulls in the orchestration glue automatically, and a Runner that installs the binary is immediately able to execute virtual prototypes using it.
The executable-name contract. The binary package installs the simulator executable under
exactly the name that the simulator class passes as executable= (e.g. simb_corundum ↔
CorundumVerilatorNICSim, simb_i40e_bm ↔ I40eNicSim, simb_net_switch ↔
SwitchNet). Resolution happens purely through $PATH — there is no registry. By convention,
SimBricks-specific binaries are prefixed with simb_.
The Python packages use implicit namespace packaging: the simbricks/ and
simbricks/components/ directories contain no __init__.py, only the leaf package does.
This is what lets independently installed component packages merge into the single
simbricks.components.* import tree. The pyproject.toml uses poetry-core, e.g.:
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "simbricks-i40e-sys-py"
version = "0.5.0"
packages = [ { include = "simbricks" } ]
[tool.poetry.dependencies]
simbricks-orchestration = ">=0.5.0"
2.4.3.3. Versioning#
Each repository’s conda-recipes/conda_build_config.yaml holds simbricks_version as the
single source of truth for the versions of all packages built from that repository, and drives the
== pins between them. It must be kept in sync by hand with the version fields in the
pyproject.toml files. Dependencies on packages from other repositories (simbricks-lib,
simbricks-orchestration, simbricks-utils) use independent >= bounds.
2.4.3.4. Building and publishing#
Each repository’s Makefile provides a uniform set of targets:
make conda-packages— build all conda packages of the repo withconda build, resolving external SimBricks dependencies from the public channel (-c https://conda.simbricks.io/latest). Development builds resolve againstlatest; release builds are made againststable. Build the recipes sequentially (do not pass-j), so each build finds the packages produced by the previous one in the local channel.make <x>-build/make <x>-install— build/install the simulator binary directly, for local development without going through conda packaging. Point the build at your installed SimBricks headers/libraries, e.g.make <x>-build SIMBRICKS_INC_DIR="$CONDA_PREFIX/include" SIMBRICKS_LIB_DIR="$CONDA_PREFIX/lib".make python-develop(or<x>-python-develop) — editablepip install -eof the Python package(s) for iterating on the orchestration glue.make pypi-build/make pypi-publish— build/publish the Python packages to PyPI via poetry (releases only).
Continuous integration is shared: every component repository’s
.github/workflows/conda-packages.yml is a six-line caller of the reusable workflow in
ci-workflows, which builds the packages in the SimBricks conda build container and uploads
them to the channel — pushes to main publish to the latest channel, pushes to release
publish to stable (and additionally publish the Python packages to PyPI).
2.4.3.5. Guest-side pieces (drivers, tools)#
Some integrations also need software inside the simulated machine — e.g. the Corundum mqnic
kernel driver, or gem5’s m5 guest tool. These are not conda packages: they must be baked
into the disk image the virtual prototype boots. The convention is that the component repository
provides Makefile targets for building/installing them in-guest (e.g. make driver-install),
and a small install script for the image-builder tool references those targets (see
Building Disk Images and, as examples, examples/corundum/install-mqnic.sh and
examples/gem5/install-m5.sh in the image-builder repository). The pre-built base image
already contains the mqnic driver and the m5 tool.
2.4.3.6. Making a simulator available on Runners#
Because the binary package carries its Python glue as a dependency, provisioning a Runner for your
simulator is a one-liner in its environment: micromamba install simbricks-<x>-...-bin. For the
standard set of simulators, the pre-built simbricks/simbricks-executor Docker image already
includes everything (see Using Pre-Built Docker Images). For custom simulators, install your package
into the Runner’s environment — or run a dedicated fragment executor for it and select it from
your scripts with fragment.fragment_executor_tag (see Runners).