Skip to content

Add USE_VENDORED_JSON option to use the system nlohmann_json (fixes #959) - #1235

Merged
facontidavide merged 2 commits into
masterfrom
feature/system-json
Oct 10, 2026
Merged

facontidavide merged 2 commits into
masterfrom
feature/system-json

Conversation

@facontidavide

Copy link
Copy Markdown
Collaborator

Fixes #959. Supersedes the draft #1080, reworked so that the default build doesn't change at all.

Problem

BT.CPP bundles nlohmann/json 3.11.3, and its public headers include it unconditionally. A user that also includes a system nlohmann/json ends up with two versions behind the same include guard. Whichever comes first wins, and the result is mismatched types or link errors (#959).

Change

The new option -DUSE_VENDORED_JSON=OFF (default ON) builds against find_package(nlohmann_json 3.10):

  • Headers: the 4 public headers that include JSON (blackboard.h, bt_factory.h, json_export.h, loggers/groot2_protocol.h) include <nlohmann/json.hpp> when BTCPP_SYSTEM_JSON is defined.
  • Exports: OFF builds export BTCPP_SYSTEM_JSON and nlohmann_json::nlohmann_json as PUBLIC, through both the CMake config (find_dependency) and ament (ament_export_dependencies). Users of the target therefore see the same version the library was built with.
  • Install: OFF builds don't install the bundled header. A user who bypasses the CMake target (plain include dirs, Makefiles, Bazel) gets a compile error instead of silently mixing two versions.
  • Tests: the 2 tests that included contrib/json.hpp directly now get nlohmann::json from the BT.CPP headers, like any user would.
  • CI: a new system-json job builds and tests OFF on Ubuntu 22.04 with apt's nlohmann_json 3.10.5, the oldest supported version.
  • Docs: a short note in the README.

Differences from #1080

#1080 defined BTCPP_VENDORED_JSON in the default build and fell back to <nlohmann/json.hpp> when the macro was missing. Any user that doesn't link the CMake target, for example through the plain variables exported for ROS, Makefiles or Bazel, would have silently switched to the system nlohmann_json while the library was still built with the bundled one. That is the #959 bug again, hitting people who never touched the option.

This PR flips the polarity: only OFF defines a macro, and the default path includes the bundled header exactly as today. It also drops the stray FIXME.md, fixes the 2 test includes, adds the ament export, and adds the CI job.

Compatibility

  • Default build (ON): API and ABI unchanged. With master's headers and with this branch's, bt_factory.h + json_export.h + groot2_protocol.h preprocess to byte-identical output.
  • OFF: nlohmann::json appears in public signatures (e.g. ExportBlackboardToJSON), so an OFF build has a different ABI. Everything linking it must be built against the same nlohmann_json. Binary packages (ROS debs, conda, vcpkg) keep the bundled copy.
  • No behavior change in either mode.

Testing

Configuration Result
Default (ON), nlohmann 3.11.3 543 passed, 2 locale tests skipped (as on master)
OFF, system nlohmann 3.11.3 543 passed, 2 skipped
OFF, Ubuntu 22.04 container with apt nlohmann 3.10.5 (same steps as the new CI job) 546/546 ctest entries passed, 2 skipped
Which header each TU used (ninja -t deps) ON: 115 TUs use the bundled copy, 0 the system one. OFF: 0 bundled, 115 system
Consumer via find_package + BT::behaviortree_cpp, ON and OFF installs builds and runs. OFF gets -DBTCPP_SYSTEM_JSON from the target
Consumer with plain g++ -I<prefix>/include (no CMake) ON: works as today. OFF: fatal error: behaviortree_cpp/contrib/json.hpp: No such file or directory
ament OFF install (ROS 2 Lyrical) + consumer via behaviortree_cpp::behaviortree_cpp nlohmann_json listed in the exported dependencies; builds and runs
pre-commit (clang-format, clang-tidy, codespell) with ON and OFF compile commands passed

🤖 Generated with Claude Code

facontidavide and others added 2 commits October 10, 2026 17:38
)

BT.CPP bundles nlohmann/json 3.11.3 and its public headers include it
unconditionally. A user that also includes a system nlohmann/json gets two
versions behind the same include guard: whichever comes first wins, and the
result is mismatched types or link errors (#959).

With -DUSE_VENDORED_JSON=OFF the library uses find_package(nlohmann_json 3.10):
- the public headers include <nlohmann/json.hpp> when BTCPP_SYSTEM_JSON is
  defined. The library exports that definition together with the
  nlohmann_json target (CMake config and ament), so its users see the same
  version it was built with;
- the bundled header is not installed: a user that bypasses the CMake target
  (plain include dirs, Makefiles, Bazel) fails to compile, instead of
  silently mixing two versions;
- the tests get nlohmann::json from the BT.CPP headers, like any user,
  instead of including the bundled copy directly.

The default (ON) is unchanged: it needs no new definition and the headers
preprocess to the same code, so the API and ABI are the same.

nlohmann::json is part of the public API, so an OFF build has a different ABI
than the default one: everything linking it must use the same nlohmann_json.

A new CI job builds and tests OFF on Ubuntu 22.04 with apt's nlohmann_json
3.10.5, the oldest supported version.

Supersedes #1080.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
'cmake --build --parallel' without a number runs an unbounded 'make -j' with
the default generator: the runner ran out of memory and was shut down.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@facontidavide
facontidavide merged commit 03ec870 into master Oct 10, 2026
17 of 18 checks passed
@facontidavide
facontidavide deleted the feature/system-json branch October 10, 2026 17:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Version conflict with embedded nlohmann::json can cause undefined behavior

1 participant