From 57953505506b2ce52e5073e3cf68fab79b6c6b32 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Sat, 10 Oct 2026 17:38:38 +0200 Subject: [PATCH 1/2] Add USE_VENDORED_JSON option to use the system nlohmann_json (fixes #959) 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 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 --- .github/workflows/cmake_ubuntu.yml | 21 +++++++++++++++++++ CMakeLists.txt | 19 ++++++++++++++++- README.md | 5 +++++ cmake/Config.cmake.in | 5 +++++ cmake/ament_build.cmake | 3 +++ include/behaviortree_cpp/blackboard.h | 4 ++++ include/behaviortree_cpp/bt_factory.h | 4 ++++ include/behaviortree_cpp/json_export.h | 7 ++++++- .../loggers/groot2_protocol.h | 4 ++++ tests/groot2_test_utils.hpp | 1 - tests/gtest_groot2_publisher_integration.cpp | 2 -- 11 files changed, 70 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cmake_ubuntu.yml b/.github/workflows/cmake_ubuntu.yml index 0f2674256..4948775df 100644 --- a/.github/workflows/cmake_ubuntu.yml +++ b/.github/workflows/cmake_ubuntu.yml @@ -50,6 +50,27 @@ jobs: - name: run test (Linux) run: ctest --test-dir build/${{env.BUILD_TYPE}} + # USE_VENDORED_JSON=OFF, with the oldest supported nlohmann_json (3.10.5 on 22.04) + system-json: + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v7 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y nlohmann-json3-dev libzmq3-dev libsqlite3-dev libgtest-dev + + - name: Configure CMake + run: cmake -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DUSE_VENDORED_JSON=OFF + + - name: Build + run: cmake --build build --parallel + + - name: run test + run: ctest --test-dir build --output-on-failure + coverage: runs-on: ubuntu-24.04 diff --git a/CMakeLists.txt b/CMakeLists.txt index ce00cdea0..f82c6bf52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ option(FORCE_STATIC_LINKING "Force static linking of all dependencies" OFF) option(USE_VENDORED_CPPZMQ "Use the bundled version of cppzmq" ON) option(USE_VENDORED_FLATBUFFERS "Use the bundled version of flatbuffers" ON) +option(USE_VENDORED_JSON "Use the bundled version of nlohmann/json" ON) option(USE_VENDORED_MINICORO "Use the bundled version of minicoro" ON) option(USE_VENDORED_MINITRACE "Use the bundled version of minitrace" ON) @@ -133,6 +134,10 @@ else() find_package(flatbuffers REQUIRED) endif() +if(NOT USE_VENDORED_JSON) + find_package(nlohmann_json 3.10 REQUIRED) +endif() + if(USE_VENDORED_MINICORO) add_subdirectory(3rdparty/minicoro) else() @@ -259,6 +264,12 @@ target_include_directories(${BTCPP_LIBRARY} target_compile_definitions(${BTCPP_LIBRARY} PUBLIC BTCPP_LIBRARY_VERSION="${PROJECT_VERSION}") +if(NOT USE_VENDORED_JSON) + # The public headers include instead of the bundled copy + target_compile_definitions(${BTCPP_LIBRARY} PUBLIC BTCPP_SYSTEM_JSON) + target_link_libraries(${BTCPP_LIBRARY} PUBLIC nlohmann_json::nlohmann_json) +endif() + target_compile_features(${BTCPP_LIBRARY} PUBLIC cxx_std_17) if(MSVC) @@ -327,8 +338,14 @@ INSTALL(TARGETS ${BTCPP_LIBRARY} INCLUDES DESTINATION ${BTCPP_INCLUDE_DESTINATION} ) +if(NOT USE_VENDORED_JSON) + # Don't install the bundled copy: a user that doesn't get BTCPP_SYSTEM_JSON + # must fail to compile, rather than mix two versions of nlohmann::json + set(BTCPP_INSTALL_EXCLUDE REGEX "contrib/json\\.hpp$" EXCLUDE) +endif() + INSTALL( DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION ${BTCPP_INCLUDE_DESTINATION} - FILES_MATCHING PATTERN "*.h*") + FILES_MATCHING PATTERN "*.h*" ${BTCPP_INSTALL_EXCLUDE}) export_btcpp_package() diff --git a/README.md b/README.md index 02b3f045d..12b824f61 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,11 @@ cmake -S . -B build_release cmake --build build_release --parallel ``` +BT.CPP includes its own copy of [nlohmann/json](https://github.com/nlohmann/json). +To use the one installed in your system instead (version 3.10 or newer), add `-DUSE_VENDORED_JSON=OFF`. +Since `nlohmann::json` is part of the public API, everything that links BT.CPP must then be +compiled with that same version. + If you want to build in a [pixi](https://pixi.sh/) project (conda virtual environment). ``` pixi run build diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in index eaed471b8..8c0e0a250 100644 --- a/cmake/Config.cmake.in +++ b/cmake/Config.cmake.in @@ -1,5 +1,10 @@ @PACKAGE_INIT@ +if(NOT "@USE_VENDORED_JSON@") + include(CMakeFindDependencyMacro) + find_dependency(nlohmann_json 3.10) +endif() + include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") set(@PROJECT_NAME@_TARGETS "BT::@PROJECT_NAME@") diff --git a/cmake/ament_build.cmake b/cmake/ament_build.cmake index 6e74556e3..7ce07e7d4 100644 --- a/cmake/ament_build.cmake +++ b/cmake/ament_build.cmake @@ -14,6 +14,9 @@ set( BTCPP_EXTRA_LIBRARIES ) ament_export_dependencies(ament_index_cpp) +if(NOT USE_VENDORED_JSON) + ament_export_dependencies(nlohmann_json) +endif() # The ROS build farm (bloom/debhelper) configures with # -DCMAKE_INSTALL_LIBDIR=lib/, but ament tooling diff --git a/include/behaviortree_cpp/blackboard.h b/include/behaviortree_cpp/blackboard.h index 95289a4be..b4e7f90c8 100644 --- a/include/behaviortree_cpp/blackboard.h +++ b/include/behaviortree_cpp/blackboard.h @@ -1,7 +1,11 @@ #pragma once #include "behaviortree_cpp/basic_types.h" +#ifdef BTCPP_SYSTEM_JSON +#include +#else #include "behaviortree_cpp/contrib/json.hpp" +#endif #include "behaviortree_cpp/exceptions.h" #include "behaviortree_cpp/utils/locked_reference.hpp" #include "behaviortree_cpp/utils/polymorphic_cast_registry.hpp" diff --git a/include/behaviortree_cpp/bt_factory.h b/include/behaviortree_cpp/bt_factory.h index 0ba3c86bb..9919b4307 100644 --- a/include/behaviortree_cpp/bt_factory.h +++ b/include/behaviortree_cpp/bt_factory.h @@ -15,7 +15,11 @@ #define BT_FACTORY_H #include "behaviortree_cpp/behavior_tree.h" +#ifdef BTCPP_SYSTEM_JSON +#include +#else #include "behaviortree_cpp/contrib/json.hpp" +#endif #include "behaviortree_cpp/contrib/magic_enum.hpp" #include "behaviortree_cpp/utils/polymorphic_cast_registry.hpp" diff --git a/include/behaviortree_cpp/json_export.h b/include/behaviortree_cpp/json_export.h index e840dae19..c14447091 100644 --- a/include/behaviortree_cpp/json_export.h +++ b/include/behaviortree_cpp/json_export.h @@ -3,8 +3,13 @@ #include "behaviortree_cpp/basic_types.h" #include "behaviortree_cpp/utils/safe_any.hpp" -// Use the version nlohmann::json embedded in BT.CPP +// Use the version of nlohmann::json embedded in BT.CPP, +// or the system one if BT.CPP was built with USE_VENDORED_JSON=OFF +#ifdef BTCPP_SYSTEM_JSON +#include +#else #include "behaviortree_cpp/contrib/json.hpp" +#endif namespace BT { diff --git a/include/behaviortree_cpp/loggers/groot2_protocol.h b/include/behaviortree_cpp/loggers/groot2_protocol.h index 1e3e51545..3ccc7b20d 100644 --- a/include/behaviortree_cpp/loggers/groot2_protocol.h +++ b/include/behaviortree_cpp/loggers/groot2_protocol.h @@ -1,7 +1,11 @@ #pragma once #include "behaviortree_cpp/basic_types.h" +#ifdef BTCPP_SYSTEM_JSON +#include +#else #include "behaviortree_cpp/contrib/json.hpp" +#endif #include #include diff --git a/tests/groot2_test_utils.hpp b/tests/groot2_test_utils.hpp index 8362f1264..e63cdb1e0 100644 --- a/tests/groot2_test_utils.hpp +++ b/tests/groot2_test_utils.hpp @@ -10,7 +10,6 @@ #include #include -#include #include namespace Groot2Test diff --git a/tests/gtest_groot2_publisher_integration.cpp b/tests/gtest_groot2_publisher_integration.cpp index 3a794b8bb..41b1c4625 100644 --- a/tests/gtest_groot2_publisher_integration.cpp +++ b/tests/gtest_groot2_publisher_integration.cpp @@ -8,8 +8,6 @@ #include #include -#include - namespace { constexpr auto kHookTreeXml = R"( From 9221b1d4aae3331dbc0fe022d0d2e849dfeae779 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Sat, 10 Oct 2026 17:49:02 +0200 Subject: [PATCH 2/2] CI: limit the system-json build to nproc jobs '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 --- .github/workflows/cmake_ubuntu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake_ubuntu.yml b/.github/workflows/cmake_ubuntu.yml index 4948775df..f42385a67 100644 --- a/.github/workflows/cmake_ubuntu.yml +++ b/.github/workflows/cmake_ubuntu.yml @@ -66,7 +66,7 @@ jobs: run: cmake -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DUSE_VENDORED_JSON=OFF - name: Build - run: cmake --build build --parallel + run: cmake --build build --parallel $(nproc) - name: run test run: ctest --test-dir build --output-on-failure