Project
Contributing
FairMQ is developed on GitHub. This page tells you how to get the code, how to build and test it, and which rules your changes must follow. It also tells you how to report a problem and how to work on this documentation.
Repository and branches
Section titled “Repository and branches”The repository is FairRootGroup/FairMQ on GitHub.
| Branch or tag | Content |
|---|---|
dev |
The development branch. Send your pull requests to dev. |
master |
The latest release. It is the default branch on GitHub. |
v1.11.0, v1.10.1, … |
The releases. The release tags are on commits of dev. |
The release notes list the changes of each release and the changes on dev that are not released yet.
Get the code
Section titled “Get the code”git clone -b dev https://github.com/FairRootGroup/FairMQ fairmq_sourceDo not make a shallow clone. The build reads the version of FairMQ from the Git tags (see Version).
The bundled packages GoogleTest, PicoSHA2 and FairCMakeModules are Git submodules in extern/. The configure step gets the submodules that it needs with git submodule update --init.
Build and test
Section titled “Build and test”-
Install the dependencies. See Install. To install them with Spack, use the file
spack.yamlof the repository:Terminal window spack --env fairmq_source installspack env activate fairmq_source -
Configure a debug build with the tests:
Terminal window cmake -S fairmq_source -B fairmq_build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON -
Build:
Terminal window cmake --build fairmq_build -
Run all tests:
Terminal window ctest --test-dir fairmq_build --output-on-failure --parallel "$(nproc)"
The debug build compiles with -Wshadow -Wall -Wextra -Wpedantic. Your change must compile without warnings, because the CI job static-analysis fails on each warning.
Select tests
Section titled “Select tests”The test suites are GoogleTest programs in test/: Channel, Device, EventManager, MemoryResources, Message, Parts, Plugins, PluginServices, PluginsPrelinked, Poller, Properties, Protocols, Region, StateMachine, Tools and Transport. CTest gives each test the name <suite>.<test>, for example Channel.ConnectedPeers.Pair/zeromq. Many tests run once for each transport and have the transport in the name. The suite Tools tests the helpers in fair::mq::tools. To test your own devices in your project, see Test your devices.
The examples add tests with names like Example.1-1.zeromq and Example.builtin-devices.multipart.shmem.
The tests have no CTest labels. Select tests with a regular expression on the name:
ctest --test-dir fairmq_build -R '^Channel\.'ctest --test-dir fairmq_build -R 'shmem'ctest --test-dir fairmq_build -R '^Example\.'The first command runs the suite Channel. The second command runs all tests with shmem in the name. The third command runs the tests of the examples.
Each suite is a program test/testsuite_<suite> in the build directory. CTest runs it in the build directory. To run some tests of a suite without CTest, give a GoogleTest filter:
cd fairmq_buildtest/testsuite_Channel --gtest_filter='ConnectedPeers.*'CTest stops a test after its timeout: 20 s for most suites, 5 s for Tools, and 30 s for the tests of the examples.
Sanitizers and static analysis
Section titled “Sanitizers and static analysis”The CI also builds FairMQ with sanitizers and with static analysis. Use the same settings to find problems before you send a change:
-
Address, leak and undefined behavior sanitizers, as the CI job
asan+lsan+ubsan:Terminal window cmake -S fairmq_source -B build_asan -G Ninja -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON \-DENABLE_SANITIZER_ADDRESS=ON -DENABLE_SANITIZER_LEAK=ON \-DENABLE_SANITIZER_UNDEFINED_BEHAVIOR=ON -DFAIRMQ_DEBUG_MODE=ON \-DCMAKE_CXX_FLAGS="-O1 -fno-omit-frame-pointer" -
Static analysis, as the CI job
static-analysis. The build runsclang-tidyandcpplinton each file that it compiles, if CMake finds these programs.Terminal window cmake -S fairmq_source -B build_tidy -G Ninja -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON \-DRUN_STATIC_ANALYSIS=ON
CMake options and variables describes all sanitizer options and the static analysis options.
Add a test
Section titled “Add a test”The tests are GoogleTest programs. test/CMakeLists.txt makes one program for each suite with the function add_testsuite() of cmake/GTestHelper.cmake.
-
Write the tests in a source file in the folder of the suite, for example
test/poller/_poller.cxx. Start the file with the license header. -
Add the file to the
SOURCESof the suite intest/CMakeLists.txt. For a new suite, make a new folder and add a call ofadd_testsuite(). -
Configure with
-DBUILD_TESTING=ON, and build. After the build, CMake runs the suite program and registers each of its tests in CTest (gtest_discover_tests()). -
Run the new tests with
ctest -R. See Run the new test.
This is the suite Poller in test/CMakeLists.txt:
add_testsuite(Poller SOURCES ${CMAKE_CURRENT_BINARY_DIR}/runner.cxx poller/_poller.cxx
LINKS FairMQ INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} TIMEOUT 20 ${definitions} ${environment})${definitions} and ${environment} add the compile definitions and the test environment that test/CMakeLists.txt sets at the top of the file.
The arguments of add_testsuite()
Section titled “The arguments of add_testsuite()”| Argument | What it does |
|---|---|
<name> |
The name of the suite. The program is test/testsuite_<name> in the build directory. CTest puts <name>. in front of each test name. |
SOURCES <file>… |
The source files. Add ${CMAKE_CURRENT_BINARY_DIR}/runner.cxx. It contains the main() of the suite. |
LINKS <lib>… |
The libraries. The function adds GTest::GTest. The suites link FairMQ, the library target of the build. |
DEPENDS <target>… |
Targets that CMake must build before the suite, for example testhelper_runTestDevice. |
INCLUDES <dir>… |
The include directories. The suites need ${CMAKE_CURRENT_SOURCE_DIR} for runner.h and the headers in test/helper/, and ${CMAKE_CURRENT_BINARY_DIR} for the generated TestEnvironment.h. |
DEFINITIONS <def>… |
Compile definitions. |
ENVIRONMENT <var>=<op>:<value>… |
Changes to the environment of each test, in the format of the CTest property ENVIRONMENT_MODIFICATION. CMake older than 3.22 ignores this argument. |
TIMEOUT <seconds> |
The CTest timeout of each test. The default is 10 s. Most suites set 20 s. |
RUN_SERIAL <ON/OFF> |
ON: CTest runs no other test at the same time as a test of this suite. The default is OFF. No suite sets it at the moment. |
add_testhelper(<name> …) makes the program testhelper_<name>, which is not a test. It takes SOURCES, LINKS, DEPENDS, INCLUDES and DEFINITIONS. add_testlib(<name> …) makes a shared library. It takes the same arguments, and also HIDDEN (hidden symbol visibility) and VERSION. The suite Plugins uses two of these libraries as plugins.
Run a test for each transport
Section titled “Run a test for each transport”The header test/helper/TransportParam.h makes a GoogleTest test run once for each transport. Write the test with TEST_P() for the fixture TransportTest. Then give the transports to INSTANTIATE_TEST_SUITE_P():
auto RunPoller(TransportParam const& param, int pollType) -> void{ auto const& transport = param.transport; IpcAddress const data1("poller_data1"); IpcAddress const data2("poller_data2");
auto const results = RunDevices({ {"pollout_" + transport, "name=data1,type=push,method=bind,address=" + data1.str() + " name=data2,type=push,method=bind,address=" + data2.str()}, {"pollin_" + transport, "name=data1,type=pull,method=connect,address=" + data1.str() + " name=data2,type=pull,method=connect,address=" + data2.str(), "--poll-type " + to_string(pollType)}, }, RunOptions(transport, param.DeviceArgs()));
for (auto const& result : results) { EXPECT_TRUE(Succeeded(result, transport)); }}
using Poller = TransportTest;
TEST_P(Poller, Subchannel){ RunPoller(GetParam(), 0);}
TEST_P(Poller, Channel){ RunPoller(GetParam(), 1);}
INSTANTIATE_TEST_SUITE_P(, Poller, AllTransports(), TransportParamName());| Values | Transports | Test name suffix |
|---|---|---|
AllTransports() |
zeromq, shmem, and shmem with --shm-metadata-msg-size 2048 |
/zeromq, /shmem, /shmem_expanded_metadata |
BasicTransports() |
zeromq and shmem |
/zeromq, /shmem |
ShmTransports() |
shmem, and shmem with --shm-metadata-msg-size 2048 |
/shmem, /shmem_expanded_metadata |
testing::Values(TransportParam("zeromq")) |
zeromq only |
/zeromq |
TransportParamName() gives the suffix. GetParam().transport is the name of the transport. GetParam().DeviceArgs() gives the extra command-line options of a device process for this parameter. For example, the suite Poller has the tests Poller.Poller.Subchannel/zeromq and Poller.Poller.Channel/shmem_expanded_metadata.
Tests with device processes
Section titled “Tests with device processes”RunDevices() of test/helper/Devices.h starts one process of testhelper_runTestDevice for each DeviceSpec. All processes get the same new session and the transport of RunOptions. The default control mode is static. The function waits for all processes. It kills a process that runs longer than RunOptions::timeout, by default 15 s.
The prefix of the device ID selects the device class, in getDevice() of test/helper/runTestDevice.cxx. For example, push_zeromq starts the class Push. A test device derives from TestDevice (test/helper/devices/TestDevice.h). At the end of its test, it calls Succeed(), which logs test device succeeded, transport: and the transport name. Succeeded() checks the exit code 0 and this log line.
To add a test device:
-
Write a header
test/helper/devices/Test<Name>.hwith a class that derives fromTestDevice. -
Include the header in
test/helper/runTestDevice.cxx. IngetDevice(), return your class for a new ID prefix. -
Add the header to the
SOURCESofadd_testhelper(runTestDevice …)intest/CMakeLists.txt. -
Give your suite the argument
DEPENDS testhelper_runTestDevice.
Run the new test
Section titled “Run the new test”Build, and run the suite with CTest:
cmake --build fairmq_buildctest --test-dir fairmq_build -R '^Poller\.' --output-on-failureTo see the test names without running the tests, add -N. To run the tests of one transport, select the suffix, for example -R '^Poller\..*/shmem$'. To run the program directly, see Select tests.
Add an example
Section titled “Add an example”Each example is a folder in examples/. Most examples use the function add_example() of examples/CMakeLists.txt. It builds the devices, writes the scripts, registers the tests, and installs the programs and the start scripts. The examples custom-controller, dds, n-m and qc have their own CMake code.
Example 1-1 has these files:
Directoryexamples/
- CMakeLists.txt calls add_subdirectory(1-1)
Directory1-1/
- CMakeLists.txt
- sampler.cxx
- sink.cxx
- fairmq-start-ex-1-1.sh.in the start script
- test-ex-1-1.sh.in the test script
- README.md
-
Make a folder
examples/<name>/. -
Write a file
<device>.cxxfor each device, withfairmq/runDevice.h. Seeexamples/1-1/sampler.cxx. The test script needs devices that stop by themselves. The devices of example 1-1 have the option--max-iterationsfor this. -
Write
examples/<name>/CMakeLists.txtwith a call ofadd_example(). Example 1-1 has this call:examples/1-1/CMakeLists.txt:9–9examples/1-1/CMakeLists.txt add_example(NAME 1-1 DEVICE sampler sink) -
Write the start script
fairmq-start-ex-<name>.sh.inand the test scripttest-ex-<name>.sh.in. See The scripts. -
Add
add_subdirectory(<name>)toexamples/CMakeLists.txt. -
Configure with
-DBUILD_TESTING=ON.BUILD_EXAMPLESisONby default. Build, and run the tests of the example:Terminal window ctest --test-dir fairmq_build -R '^Example\.<name>\.' --output-on-failure
The arguments of add_example()
Section titled “The arguments of add_example()”| Argument | What it does |
|---|---|
NAME <name> |
Required. The name of the example. Without it, CMake stops with NAME arg is required. |
DEVICE <device>… |
For each device, the program fairmq-ex-<name>-<device> from the file <device>.cxx, linked to FairMQ. |
SCRIPT <script>… |
The start scripts fairmq-start-ex-<script>.sh.in. The default is one script with the name of the example. |
CONFIG |
Copies ex-<name>.json to the build directory of the example, and installs it into share/fairmq/. The scripts get @EX_CONF_DIR@. |
TRANSPORT <transport>… |
The transports of the tests. The default is zeromq and shmem. |
VARIANT <variant>… |
One test for each variant and transport, Example.<name>.<variant>.<transport>. The test script gets the variant as its second argument. |
NO_TRANSPORT |
One test, without the transport argument. Known defect: the test name is Example.<name>., with a dot at the end. |
NO_TEST |
No test script and no test. |
Other arguments stop CMake with Unexpected unparsed arguments. Without VARIANT and NO_TRANSPORT, the tests are Example.<name>.<transport>, for example Example.1-1.zeromq and Example.1-1.shmem.
The scripts
Section titled “The scripts”CMake writes two copies of each start script. The copy in the build directory of the example has the name fairmq-start-ex-<script>.sh. The copy fairmq-start-ex-<script>.sh_install has the paths of the installation, and the install step copies it to bin/ as fairmq-start-ex-<script>.sh. CMake replaces these variables:
| Variable | Build directory | Installation |
|---|---|---|
@EX_BIN_DIR@ |
The build directory of the example | <prefix>/bin |
@FAIRMQ_BIN_DIR@ |
<build>/fairmq |
<prefix>/bin/fairmq |
@EX_CONF_DIR@ (with CONFIG) |
The build directory of the example | <prefix>/share/fairmq |
CTest runs test-ex-<name>.sh <transport>, or test-ex-<name>.sh <transport> <variant>, with a timeout of 30 s. The test passes if the script exits with 0. The install step does not install the test script. This is the test script of example 1-1:
#!/bin/bash
set -e
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
transport="zeromq"
if [[ $1 =~ ^[a-z]+$ ]]; then transport=$1fi
session="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"chan="data"chanAddr="/tmp/fmq_$session""_""$chan""_""$transport"
# setup a trap to kill everything if the test fails/timeoutstrap 'set +e; kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; wait $SAMPLER_PID; wait $SINK_PID; rm $chanAddr; exit 0' TERM
SAMPLER="fairmq-ex-1-1-sampler"SAMPLER+=" --id sampler1"SAMPLER+=" --rate 1"SAMPLER+=" --transport $transport"SAMPLER+=" --verbosity veryhigh"SAMPLER+=" --session $session"SAMPLER+=" --shm-segment-size 100000000"SAMPLER+=" --shm-monitor true"SAMPLER+=" --control static --color false"SAMPLER+=" --max-iterations 1"SAMPLER+=" --channel-config name=$chan,type=push,method=bind,address=ipc://$chanAddr,rateLogging=0"@CMAKE_CURRENT_BINARY_DIR@/$SAMPLER &SAMPLER_PID=$!
SINK="fairmq-ex-1-1-sink"SINK+=" --id sink1"SINK+=" --transport $transport"SINK+=" --verbosity veryhigh"SINK+=" --session $session"SINK+=" --shm-segment-size 100000000"SINK+=" --shm-monitor true"SINK+=" --control static --color false"SINK+=" --max-iterations 1"SINK+=" --channel-config name=$chan,type=pull,method=connect,address=ipc://$chanAddr,rateLogging=0"@CMAKE_CURRENT_BINARY_DIR@/$SINK &SINK_PID=$!
# wait for sampler and sink to finishwait $SAMPLER_PIDwait $SINK_PID
set +erm $chanAddrexit 0The script starts both devices with --control static, --max-iterations 1 and a new session from fairmq-uuid-gen. The ipc address contains the session and the transport, so that tests that run at the same time do not use the same address. With set -e, a device that fails also fails the test.
Code style
Section titled “Code style”- Format:
.clang-formatin the repository root sets the format. It is based on the Mozilla style, with an indent of 4 spaces, no tabs and a column limit of 100. The pointer alignment follows the most frequent style in the file, with left alignment (int* p) as the fallback. The existing code does not follow this file everywhere. Format only the lines that you change, for example withgit clang-format. - Checks:
.clang-tidyenables a selected set ofmisc,modernize,performanceandreadabilitychecks. WithRUN_STATIC_ANALYSIS=ON, the build runs them if CMake findsclang-tidy. The CI jobstatic-analysisuses this option. - Language: C++17. Put new code in the namespace
fair::mq. Do not use the deprecatedFairMQ*names in new code. See Move from the FairMQ* names. - License header: each source file starts with the license header of FairMQ. Copy the header from a file in the same directory. See License and citation.
The repository has no .editorconfig file.
Commit messages
Section titled “Commit messages”The commit titles use the Conventional Commits format type(scope): summary. The file .gitlint configures the tool gitlint with these rules:
- The type is one of
fix,feat,build,ci,docs,style,refactor,perfandtest. - The title has a maximum of 75 characters.
- A body is optional.
The scope is optional. It names the part of FairMQ, for example shmem, zeromq, tools or test. Titles from the history:
fix(shmem): lock the region mutexes in one orderfeat(tools): add an execute() overload with a deadlinetest(channel): add tcp tests with and without auto-bindci: run the tests in parallelWrite the summary in the imperative mood. Use the body to tell what changed and why. The history marks a breaking change with ! after the type or the scope, for example feat!: or feat(ofi)!:. Most of these commits also have a paragraph BREAKING CHANGE in the body. The pull request template also links the seven rules of a great commit message.
To check the message of your last commit, install gitlint and run gitlint in the repository.
Pull requests
Section titled “Pull requests”-
Fork the repository on GitHub and make a branch from
dev. -
Make your change, with tests for new behavior.
-
Add your name to
CONTRIBUTORS. See Authors and contributors. -
Rebase your branch on the latest
dev. -
Open a pull request to
dev. Describe your change, and name the issues that it is related to. The template asks you to confirm three items: the rebase ondev, your name inCONTRIBUTORSorAUTHORS, and the rules for commit messages.
The workflows are in .github/workflows/.
| Workflow | When it runs | What it does |
|---|---|---|
CI |
Each pull request, each push to master and dev |
The job build builds FairMQ with GCC 12, 13, 14 and 15 and the Spack environment test/ci/spack-latest.yaml, and with GCC 15 and test/ci/spack-boost187.yaml (Boost 1.87). It runs the tests and the install step. The job sanitizers runs the tests with the address, leak and undefined behavior sanitizers (GCC 14), and with the thread sanitizer (GCC 15). The job static-analysis builds with RUN_STATIC_ANALYSIS=ON and fails on each warning. |
Docs |
Each pull request, each push to dev, and a manual start |
Builds this site and checks its links. A push to dev also publishes the site on GitHub Pages. |
Check AUTHORS and CONTRIBUTORS in metadata |
Changes to AUTHORS, CONTRIBUTORS, codemeta.json or .zenodo.json |
Runs meta_update.py and fails if the metadata files change. |
validate codemeta |
Changes to codemeta.json |
Validates codemeta.json. |
fair-software |
Each push | Measures the compliance with the recommendations of fair-software.eu. |
Spack Buildcache |
Each week, a manual start, and a push to dev or master that changes the Spack files of the CI |
Builds the dependencies of the CI and stores them in a Spack build cache. |
The CI gets its dependencies from the Spack environments in test/ci/. Dependabot updates the GitHub Actions, the Git submodules and the npm packages of the documentation each month, with pull requests to dev.
Report a problem
Section titled “Report a problem”Report bugs and ask for features in the GitHub issues. The issue templates are Bug report, Feature request and Question / Support.
For a bug report, give the information that the template asks for:
- The steps to reproduce the problem: your environment, the CMake options and the commands.
- The expected behavior.
- The logs.
- The operating system, the compiler and the environment, for example the FairSoft version.
Authors and contributors
Section titled “Authors and contributors”The file AUTHORS lists the authors of FairMQ. The file CONTRIBUTORS lists the other people who contributed. Each line has this format, with an optional e-mail address and an optional ORCID:
Family name, Given name <e-mail> [https://orcid.org/0000-0000-0000-0000]The files codemeta.json and .zenodo.json contain the same names. After you change AUTHORS or CONTRIBUTORS, update them with the script in the repository root:
python3 meta_update.pyCommit the changed metadata files together with your change. The CI workflow Check AUTHORS and CONTRIBUTORS in metadata fails if they do not agree. License and citation tells how the project uses these names.
Work on the documentation
Section titled “Work on the documentation”This site is in the directory docs/ of the repository. It uses Astro and Starlight.
You need these programs:
- Node.js 22.23.4 or later.
enginesindocs/package.jsonsets this version. The CI uses Node.js 24. - Doxygen, for the API pages and the source pages. The CI installs the Doxygen package of Ubuntu 24.04.
Run these commands in docs/:
| Command | What it does |
|---|---|
npm ci |
Installs the packages of package-lock.json. |
npm run dev |
Starts a live preview at http://localhost:4321/FairMQ/. |
npm run build |
Builds the site in dist/. |
npm run preview |
Shows the built site from dist/. |
npm run check |
Checks all internal links and anchors in the built site. Run it after npm run build. |
node scripts/check-pages.mjs |
Checks the pages without a build: the frontmatter, the MDX syntax, the imports, the links and anchors, and the code snippets. Give page files to check only these pages. |
node scripts/print-api.mjs <name> --members |
Lists the API pages with <name> in the name, and the anchors of their members. It reads the Doxygen XML. |
npm run dev and npm run build run Doxygen first if necessary. This occurs if the XML in docs/build/doxygen/xml/ is missing or older than a .h or .cxx file in fairmq/. If Doxygen is not installed, they stop with an error.
The pages are MDX files in docs/src/content/docs/<section>/<page>.mdx. The site makes some content from the code:
- The API pages (
/api/) and the source pages (/source/) come from the headers and sources infairmq/, through the XML output of Doxygen. - The names, the types and the defaults in the tables of command-line options come from the option definitions in the code. The descriptions are in
src/data/option-texts.mjs. - Code samples come from files of the repository, through the component
Snippet. The build fails if the selected lines do not exist. Use the examples and the tests for samples, because the CI compiles them.
The pages use Simplified Technical English (ASD-STE100): short sentences, the active voice, and one word for one concept. Links to other pages are root-relative, without the base /FairMQ, and end with /, for example /concepts/states/. Each page has the link Edit page at the bottom. It opens the file of the page on GitHub.
Related pages
Section titled “Related pages”- CMake options and variables: all options of the build.
- Release notes: the changes in each release.
- License and citation: the license, the copyright holders and how to cite FairMQ.