Guidance for AI coding agents (and the humans driving them) working in
the Open MPI source tree. This file is an orientation map, not the
full rulebook: the authoritative, human-maintained documentation lives
under docs/developers/ and
docs/contributing.rst (rendered at
https://docs.open-mpi.org/). When this file and those docs disagree,
the docs win — and please fix this file.
AI-assisted contributions are welcome. But Open MPI runs on the largest supercomputers in the world and across a huge range of operating systems and hardware. We want careful, portable, performant code — not plausible-looking code that solves one problem in one environment at the expense of others. Hold yourself to the same bar as a thoughtful human contributor.
Open MPI is an open source implementation of the Message Passing Interface (MPI) specification — a high-level library for sending discrete, typed messages between processes, independent of the underlying network or OS. It also includes a run-time system that launches and manages the lifecycle of many processes across many hosts as a single MPI "job".
The code base is divided into three projects, which are strict abstraction barriers — each compiles to its own library with a one-way dependency order:
OSHMEM (liboshmem) OpenSHMEM API layer
│ depends on
OMPI (libmpi) MPI API layer + language bindings
│ depends on
OPAL (libopen-pal) portability layer (OS/arch abstractions)
- OPAL — portability primitives. Symbols prefixed
opal_/OPAL_. This is where most OS/arch#ifblocks belong. - OMPI — everything the MPI standard mandates: the language bindings
(C and several Fortran flavors) on top, MCA frameworks
underneath. Symbols prefixed
ompi_/OMPI_; only official MPI symbols getMPI_/mpi_. - OSHMEM — the OpenSHMEM API layer; sibling to OMPI, changes slowly.
Symbols prefixed
oshmem_/OSHMEM_.
Linker boundary (a real, hard error if you violate it): code in a lower layer cannot directly call functions in a higher layer. OPAL cannot call OMPI or OSHMEM; OMPI cannot call OSHMEM. The legal way for a lower layer to reach upward is a callback function pointer handed down from the higher layer. Direct upward calls fail to resolve at link time.
OPAL, OMPI, and OSHMEM are built almost entirely out of MCA plugins.
Read docs/developers/terminology.rst
and the MCA section it points to before doing real work. The hierarchy:
- Project → Framework → Component → Module (runtime instance, like a C++ object). Each level is isolated from its siblings; a framework exposes a top-level header for its public API.
- Example:
opal/mca/btl/is the BTL framework in OPAL;opal/mca/btl/tcp/is one component within it. - MCA parameters let users change behavior at run time (env var, file, CLI). Prefer adding an MCA parameter over hard-coding a constant — this is idiomatic and expected here.
From docs/developers/source-code.rst
and docs/contributing.rst:
- Prefix rule. Filenames are prefixed
<framework>_<component>. Public symbols in a component are prefixed<project>_<framework>_<component>(<project>∈mca,opal,ompi,oshmem). Non-public symbols must bestaticor otherwise kept out of global scope. When in doubt, add the prefix. - Include
<level>_config.hfirst —opal_config.h,ompi_config.h, oroshmem_config.hfor the layer you're in — as the very first#include, before any system header. - Use the
__opal_attribute_*__macros for compiler attributes.opal/include/opal_config_bottom.h, pulled in transitively byopal_config.h, defines portable wrappers —__opal_attribute_unused__,__opal_attribute_noreturn__,__opal_attribute_format__,__opal_attribute_deprecated__, and many more — that expand to the appropriate__attribute__((...))on compilers that support it and to nothing elsewhere. Reach for these (for example, to mark an unused function parameter) rather than writing a bare__attribute__or leaving a warning unaddressed. - MPI back-end code must never call public
MPI_*()APIs. The bindings are thin wrappers; call the internalompi_*routines, not the user-facing entry points. - New files need the standard copyright/license header. Copy the
multi-institution BSD header block — including the
$COPYRIGHT$and$HEADER$tokens — from a neighboring file. If you substantially change an existing file, add your copyright line to its block. #definelogical macros to0or1; never#undefthem. Test with#if FOO, not#ifdef FOO, so a misspelling is a compiler error, not a silent false.- Put constants on the left of equality tests:
if (NULL == ptr). - Always brace blocks, even one-liners. 4-space indents, never a literal tab character, in any language.
- C11 is required (Open MPI ≥ 6.0): C++-style
//comments and C99 mixed declarations are allowed and preferred. Fortran has no formal style — match the surrounding code. - Stay compiler-warning-free. Open MPI strives to build with zero compiler warnings. Do not introduce code that adds new warnings.
The MPI C/Fortran bindings are generated at build time by the Python
generator under ompi/mpi/bindings/
(bindings.py + ompi_bindings/), driven in part by official MPI
symbol/signature data pulled from the MPI Forum's pympistandard. If
you need to change a binding's behavior, change the generator,
templates, or the back-end implementation — never the emitted .c/.h
files.
3rd-party/and the git submodules (embedded OpenPMIx, and the Open MPI fork of PRRTE). Fixes belong upstream, not patched in here.- Autotools-generated output —
configure,Makefile.in,config.status, anything produced by./autogen.pl. Editconfigure.ac,Makefile.am, or the m4 inconfig/instead. - Generated MPI bindings — see the section above.
- Pre-rendered docs — shipped HTML and generated man pages. Edit the
RST sources under
docs/.
Open MPI uses the GNU Autotools (Autoconf / Automake / Libtool). From a Git clone:
./autogen.pl # regenerate the build system (one-time / after build-system changes)
./configure --prefix=/path/to/install
make -j # full builds are SLOW
make installOut-of-tree (VPATH) builds are not required, but they are often helpful for sanity checks because they avoid perturbing the source tree:
mkdir build && cd build
../configure --prefix=/path/to/install
make -jSee docs/developers/building-open-mpi.rst
and the install docs for options.
Editing the build system means regenerating it — make alone can't,
and trying will wedge the tree. If you change configure.ac or any
config/*.m4 file (including the embedded oac/Autotools macros), the
change does not take effect until the build system is regenerated. Do
not rely on a plain make: Open MPI builds in maintainer mode, so
make auto-triggers a partial in-tree Autotools regeneration that
frequently fails (e.g., unexpanded OAC_* macros, config.status
errors) and can leave the tree half-regenerated and
unbuildable. Instead, regenerate and reconfigure explicitly:
./autogen.pl
./configure <same options as the original configure>
make -jRecover the original configure invocation options from the existing
tree with ./config.status --config (or read the header of
config.log). This process is slow but mandatory after any
build-system source change — there is no safe shortcut.
Note that editing Makefile.am files do not require the full
autogen.pl + ./configure process. A simple make will regenerate
the relevant Makefile[.in] files and then complete the build
successfully.
"Did I break it?" — layered:
-
Build cleanly. A clean
makeafter your change is the baseline. Open MPI is highly configurable at build time: many components, source files, directories, and generated artifacts are selected or omitted byconfigureand Automake based on the local environment. For any change to code or documentation that might be conditionally built, verify that your configured build is actually compiling or generating the thing you changed; do not assume this can be checked only by looking for#ifblocks. -
Documentation-only changes can be narrower. If the change is wholly under
docs/, it is often enough to configure with Sphinx support and runmakein thedocs/build directory instead of doing a full build, install, smoke test, ormake check. Make sure Sphinx was really enabled byconfigure; one practical check is thatSPHINX_BUILDinconfig.statusnames a valid executable. -
Quick smoke test. After
make install, put your--prefix'sbin/on yourPATH, then build and run an example on the local host:cd examples && make # compiles against the installed mpicc/mpifort wrappers mpirun --np 2 ./hello_c # smallest launch + MPI_Init/Finalize sanity check mpirun --np 2 ./ring_c # adds real point-to-point messaging
-
Deeper validation when your environment supports it:
make checkand the programs undertest/. Be aware that the full suite and realistic MPI jobs frequently need a proper launcher and/or multiple hosts/specialized hardware — do not assume you can run all of it locally, and don't report untested code as verified.
Test across environments when you can. Portability across a wide variety of environments is a core Open MPI goal. The primary development environments are common Linux distributions and macOS, but the code is expected to run far more widely. When container-based tooling is available, it can be a practical way to reproduce, diagnose, and test user-space behavior specific to an environment you aren't running natively — for example, using Docker on macOS to exercise Linux user-space code paths. (Containers don't replace real network/hardware/launcher testing, but they're useful for OS and user-space differences.)
Add tests for new code. Whenever practical, add unit tests under
test/ that are wired into make check (and therefore run in
CI). Prefer a make check-able test over a manual one-off so the
coverage sticks and regressions are caught automatically.
Never bend a test to accommodate a bug. Do not weaken, skip, or rewrite an existing test — and do not craft a new one — merely to make buggy behavior pass. Tests encode intended behavior: when one fails, the default assumption is that the code is wrong, not the test. If you find a genuine bug in the code base, identify it, report it, and where appropriate fix it — don't paper over it in the test suite.
Performance is paramount: short-message latency and large-message bandwidth are headline metrics, along with the ability to offload work to networking/GPU hardware so the CPU can make progress elsewhere. Microseconds — sometimes nanoseconds — matter, and much of the hot path uses OS-bypass techniques talking directly to network, CUDA, and ROCm hardware.
Concrete rules for hot paths:
- Don't add allocations, locks, or branches to the critical send/receive path without a clear, measured justification.
- Guard debug output and expensive assertions behind
OPAL_ENABLE_DEBUGso release builds pay nothing for them. - Prefer an MCA parameter to a hard-coded constant when a value might need tuning per environment.
- Keep environment-specific optimization where it belongs — generally in OPAL or in the hardware-specific component — not smeared across portable MPI logic.
Don't assume you're the only agent (or person) using this clone. In
particular, if you're working in a git worktree, other worktrees may
be active against the same underlying repository at the same time. Avoid
repo-wide git commands that reach outside your own working area and can
disrupt others — for example, git worktree prune, or git stash
(which writes to the repository-wide stash ref shared by all worktrees).
Keep your git operations scoped to your own branch and worktree.
As a narrow exception, creating a new branch when you need to park
work in progress (for example, instead of git stash) is fine. Just be
careful not to collide with branches that other agents or people may be
using in the same clone — pick a clearly-scoped, unlikely-to-clash name.
Authoritative process:
docs/contributing.rst. Highlights agents must
honor:
- Sign off every commit. Each commit needs a
Signed-off-by:line per the Contributor's Declaration — usegit commit -s. Commits without it are not accepted. This applies to AI-assisted work too: the human submitter certifies the contribution. - Commit messages: a short first line saying what changed, then a
body explaining why. Open MPI does not use Conventional Commits
(
feat:/fix:prefixes) — write prose. Don't add AI tooling attribution. Wrap commit-message lines at around 75 characters. - Keep incidental fixes as their own commits. Small "drive-by" bug fixes you notice while working on something else are welcome, but it is usually best to land them as standalone commits, separate from your main change, so each can be evaluated and reviewed on its own. One logical change per commit keeps history reviewable and easy to bisect.
- Branch flow: land on
mainfirst via a GitHub pull request, then cherry-pick to the relevant release branch(es)vMAJOR.MINOR.xwith a(cherry picked from commit ...)line at the end of the commit message; usegit cherry-pick -xto add it. Open MPI always lands commits onmainand release branches through pull requests; never push directly to those branches. Never commit features directly to a release branch. Seedocs/developers/git-github.rst. - Update the docs and the changelog when user-visible behavior
changes: RST under
docs/, and a release-notes entry underdocs/release-notes/changelog/(vMAJOR.MINOR.x.rst). - In user-facing docs, the package's name is "Open MPI." When
referring to this software package as a whole in user-facing
documentation, always write the formal name Open MPI — never the
abbreviation "OMPI". ("OMPI" is correct only as the internal name of
the middle project layer (OPAL → OMPI → OSHMEM) and its
ompi_/OMPI_symbol prefix — never as a public-facing name for the package.) - Keep the LLM-friendly docs in sync. The machine-readable MPI API
artifacts (catalog, corpora, per-symbol pages, manifest) regenerate
automatically from the man-page RST and binding metadata, so they
cannot drift. The curated sources under
docs/llms-src/(the interface guide, examples, and theompi_inforuntime-introspection guide) can: when a PR changes public MPI documentation, update the affected curated files when relevant. If you change the curated examples or the JSON Schemas, regeneratespecs/llms-friendly-docs/sample-records.jsonlsomake check(which validates the artifacts) still passes. Seedocs/developers/llm-friendly-docs.rst.
| Path | What's there |
|---|---|
opal/ |
OPAL portability layer (opal/mca/ = its frameworks) |
ompi/ |
OMPI / MPI layer; ompi/mpi/ = language bindings, ompi/mca/ = frameworks |
oshmem/ |
OpenSHMEM layer |
3rd-party/ |
embedded upstreams + submodules (OpenPMIx, PRRTE fork) — don't hand-edit |
config/ |
m4 macros for Autoconf / Automake / Libtool |
docs/ |
all RST documentation (Sphinx); docs/developers/ is the dev guide |
examples/ |
small MPI example programs (good smoke tests) |
test/ |
unit / functional tests |
contrib/ |
unsupported contributed scripts and tools |
- Match the surrounding code's style and conventions — this is an old, multi-author code base with established patterns.
- Read the relevant
docs/developers/page before inventing a new pattern. - Ask on the developer mailing list / a GitHub issue for anything large
before writing it; see
docs/contributing.rst.