From 1c36c5e3d1ffc19ef8c7c4efa6775710861ef4d2 Mon Sep 17 00:00:00 2001 From: Nathan Brooks Date: Mon, 13 Jul 2026 07:30:18 -0600 Subject: [PATCH 1/4] core: wrap runtime-composed fmt format strings with fmt::runtime() On ROS Rolling (fmt >= 10) format strings must be consteval. cost_terms.cpp builds format strings by concatenating PREFIX at runtime; stage.cpp uses a generic auto... args lambda that fmt::format then can't verify at compile time. Wrap both call sites with fmt::runtime() to tell fmt these are real runtime format strings. No behavior change on humble/jazzy/kilted where fmt was more permissive. --- core/src/cost_terms.cpp | 8 ++++---- core/src/stage.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/cost_terms.cpp b/core/src/cost_terms.cpp index 9ddb94ff2..15e6c2271 100644 --- a/core/src/cost_terms.cpp +++ b/core/src/cost_terms.cpp @@ -298,7 +298,7 @@ double Clearance::operator()(const SubTrajectory& s, std::string& comment) const } }; auto collision_comment = [=](const auto& distance) { - return fmt::format(PREFIX + "allegedly valid solution collides between '{}' and '{}'", distance.link_names[0], + return fmt::format(fmt::runtime(PREFIX + "allegedly valid solution collides between '{}' and '{}'"), distance.link_names[0], distance.link_names[1]); }; @@ -313,10 +313,10 @@ double Clearance::operator()(const SubTrajectory& s, std::string& comment) const } distance = distance_data.distance; if (!cumulative) - comment = fmt::format(PREFIX + "distance {} between '{}' and '{}'", distance, distance_data.link_names[0], + comment = fmt::format(fmt::runtime(PREFIX + "distance {} between '{}' and '{}'"), distance, distance_data.link_names[0], distance_data.link_names[1]); else - comment = fmt::format(PREFIX + "cumulative distance {}", distance); + comment = fmt::format(fmt::runtime(PREFIX + "cumulative distance {}"), distance); } else { // check trajectory for (size_t i = 0; i < s.trajectory()->getWayPointCount(); ++i) { auto distance_data = check_distance(state, s.trajectory()->getWayPoint(i)); @@ -327,7 +327,7 @@ double Clearance::operator()(const SubTrajectory& s, std::string& comment) const distance += distance_data.distance; } distance /= s.trajectory()->getWayPointCount(); - comment = fmt::format(PREFIX + "average{} distance: {}", (cumulative ? " cumulative" : ""), distance); + comment = fmt::format(fmt::runtime(PREFIX + "average{} distance: {}"), (cumulative ? " cumulative" : ""), distance); } return distance_to_cost(distance); diff --git a/core/src/stage.cpp b/core/src/stage.cpp index b64ce0ab3..40c5bdd58 100644 --- a/core/src/stage.cpp +++ b/core/src/stage.cpp @@ -909,8 +909,8 @@ bool Connecting::compatible(const InterfaceState& from_state, const InterfaceSta const planning_scene::PlanningSceneConstPtr& from = from_state.scene(); const planning_scene::PlanningSceneConstPtr& to = to_state.scene(); - auto false_with_debug = [](auto... args) { - RCLCPP_DEBUG_STREAM(rclcpp::get_logger("Connecting"), fmt::format(args...)); + auto false_with_debug = [](auto fmt_str, auto&&... args) { + RCLCPP_DEBUG_STREAM(rclcpp::get_logger("Connecting"), fmt::format(fmt::runtime(fmt_str), std::forward(args)...)); return false; }; From ef696c3a11578b7690d9fa1684ba3510b06d828f Mon Sep 17 00:00:00 2001 From: Nathan Brooks Date: Mon, 13 Jul 2026 07:30:18 -0600 Subject: [PATCH 2/4] visualization: dual-support Qt5 and Qt6 Try Qt6 first, fall back to Qt5. Ubuntu Resolute removes Qt5 from the default apt indexes; humble/jazzy/kilted still have Qt5. Additive-only. qt_wrap_ui macro gets a conditional qt5/qt6 branch. --- visualization/CMakeLists.txt | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/visualization/CMakeLists.txt b/visualization/CMakeLists.txt index a220b3a62..13578a3b1 100644 --- a/visualization/CMakeLists.txt +++ b/visualization/CMakeLists.txt @@ -19,10 +19,20 @@ find_package(rviz_ogre_vendor REQUIRED) add_definitions(-DBOOST_MATH_DISABLE_FLOAT128) # Qt Stuff -find_package(Qt5 REQUIRED COMPONENTS Core Widgets) -set(QT_LIBRARIES Qt5::Widgets) +find_package(Qt6 QUIET COMPONENTS Core Widgets) +if(Qt6_FOUND) + set(QT_VERSION_MAJOR 6) +else() + find_package(Qt5 REQUIRED COMPONENTS Core Widgets) + set(QT_VERSION_MAJOR 5) +endif() +set(QT_LIBRARIES Qt${QT_VERSION_MAJOR}::Widgets) macro(qt_wrap_ui) - qt5_wrap_ui(${ARGN}) + if(QT_VERSION_MAJOR EQUAL 5) + qt5_wrap_ui(${ARGN}) + else() + qt6_wrap_ui(${ARGN}) + endif() endmacro() set(CMAKE_INCLUDE_CURRENT_DIR ON) From 9540954ee44eaf4dc04da1e5d08f819998f53efa Mon Sep 17 00:00:00 2001 From: Nathan Brooks Date: Mon, 13 Jul 2026 07:30:18 -0600 Subject: [PATCH 3/4] visualization/motion_planning_tasks: char8_t + rviz Display::update API - task_list_model.cpp, remote_task_model.cpp: reinterpret_cast around u8"..." literals so they can feed Qt tr() under C++20's char8_t distinction. - task_display.{h,cpp}: dual-signature update() override guarded on RCLCPP_VERSION_GTE(30, 0, 0). Rolling switched Display::update from (float, float) to (std::chrono::nanoseconds, std::chrono::nanoseconds); Rolling branch converts to float seconds and calls the existing body. --- .../motion_planning_tasks/src/remote_task_model.cpp | 2 +- .../motion_planning_tasks/src/task_display.cpp | 13 +++++++++++++ .../motion_planning_tasks/src/task_display.h | 6 ++++++ .../motion_planning_tasks/src/task_list_model.cpp | 4 ++-- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/visualization/motion_planning_tasks/src/remote_task_model.cpp b/visualization/motion_planning_tasks/src/remote_task_model.cpp index d28cfd60e..c84dff3cb 100644 --- a/visualization/motion_planning_tasks/src/remote_task_model.cpp +++ b/visualization/motion_planning_tasks/src/remote_task_model.cpp @@ -526,7 +526,7 @@ QVariant RemoteSolutionModel::data(const QModelIndex& index, int role) const { return item.creation_rank; case 1: if (std::isinf(item.cost)) - return tr(u8"∞"); + return tr(reinterpret_cast(u8"∞")); if (std::isnan(item.cost)) return QVariant(); return QLocale().toString(item.cost, 'f', 4); diff --git a/visualization/motion_planning_tasks/src/task_display.cpp b/visualization/motion_planning_tasks/src/task_display.cpp index 0c07a2dea..4cd01eb65 100644 --- a/visualization/motion_planning_tasks/src/task_display.cpp +++ b/visualization/motion_planning_tasks/src/task_display.cpp @@ -37,6 +37,7 @@ */ #include "task_display.h" +#include #include "task_panel.h" #include "task_list_model.h" #include "meta_task_list_model.h" @@ -184,12 +185,24 @@ void TaskDisplay::calculateOffsetPosition() { scene_node_->setOrientation(orientation); } +#if RCLCPP_VERSION_GTE(30, 0, 0) +void TaskDisplay::update(std::chrono::nanoseconds wall_dt, std::chrono::nanoseconds ros_dt) { + requestPanel(); + Display::update(wall_dt, ros_dt); + calculateOffsetPosition(); + using namespace std::chrono; + float wall_dt_s = duration_cast>(wall_dt).count(); + float ros_dt_s = duration_cast>(ros_dt).count(); + trajectory_visual_->update(wall_dt_s, ros_dt_s); +} +#else void TaskDisplay::update(float wall_dt, float ros_dt) { requestPanel(); Display::update(wall_dt, ros_dt); calculateOffsetPosition(); trajectory_visual_->update(wall_dt, ros_dt); } +#endif void TaskDisplay::changedRobotDescription() { if (isEnabled()) diff --git a/visualization/motion_planning_tasks/src/task_display.h b/visualization/motion_planning_tasks/src/task_display.h index 41fa159d1..d76d12090 100644 --- a/visualization/motion_planning_tasks/src/task_display.h +++ b/visualization/motion_planning_tasks/src/task_display.h @@ -39,6 +39,8 @@ #pragma once #include +#include +#include #include #include @@ -83,7 +85,11 @@ class TaskDisplay : public rviz_common::Display void loadRobotModel(); +#if RCLCPP_VERSION_GTE(30, 0, 0) + void update(std::chrono::nanoseconds wall_dt, std::chrono::nanoseconds ros_dt) override; +#else void update(float wall_dt, float ros_dt) override; +#endif void reset() override; void save(rviz_common::Config config) const override; void load(const rviz_common::Config& config) override; diff --git a/visualization/motion_planning_tasks/src/task_list_model.cpp b/visualization/motion_planning_tasks/src/task_list_model.cpp index 3cd646690..7b6bd1a2a 100644 --- a/visualization/motion_planning_tasks/src/task_list_model.cpp +++ b/visualization/motion_planning_tasks/src/task_list_model.cpp @@ -61,9 +61,9 @@ QVariant TaskListModel::horizontalHeader(int column, int role) { case 0: return tr("name"); case 1: - return tr(u8"✓"); + return tr(reinterpret_cast(u8"✓")); case 2: - return tr(u8"✗"); + return tr(reinterpret_cast(u8"✗")); case 3: return tr("time"); } From 61d0f9f38014a0fd30d8d5922bda9fede5bfba5d Mon Sep 17 00:00:00 2001 From: Nathan Brooks Date: Sun, 2 Aug 2026 22:46:56 -0600 Subject: [PATCH 4/4] visualization: gate Qt6 on rviz's version, and use the versionless Qt rosdep key Two corrections to the Qt5/Qt6 dual-support commit. 1. Gate on rviz_common_VERSION, not Qt6 availability find_package(Qt6 QUIET) succeeds whenever Qt6 is merely installed, which says nothing about which Qt rviz was built against -- and this package links rviz_common/rviz_default_plugins, so its Qt must match rviz's. Ubuntu 24.04 ships both, so on jazzy or kilted with qt6-base-dev present the probe does not silently mis-select; it aborts the configure, because rviz_common has already defined the versionless Qt::Core target: CMake Error at Qt6CoreVersionlessTargets.cmake:42: Some (but not all) targets in this export set were already defined. QUIET does not suppress that -- it is a FATAL_ERROR inside Qt6's own config, not a not-found condition, so the Qt5 fallback is never reached. rviz switched to Qt6 in 15.1.14: ros2/rviz#1635 (merged 2025-12-08) first shipped in that tag on 2025-12-17. Note 15.1.0 through 15.1.13 are still Qt5, so the threshold is 15.1.14 rather than 15.1. humble rviz 11.2 -> Qt5 lyrical rviz 15.2 -> Qt6 jazzy rviz 14.1 -> Qt5 rolling rviz 16.0 -> Qt6 kilted rviz 15.0 -> Qt5 rviz_common is already find_package'd above the Qt block, so the version is in scope. Also carries the QT_DIR hint for transitive deps whose find_package(QT NAMES Qt6 Qt5 ...) can mis-resolve under CMake's ascending path order. 2. package.xml still requested Qt5 unconditionally qtbase5-dev That resolves to Qt5 on every platform, so rosdep would install Qt5 while CMake selects Qt6 -- which is exactly how the collision above arises. The versionless key maps per-platform, the same move ros2/rviz#1635 made in rviz_common's own package.xml: key jammy noble * (resolute) qt-base-dev qtbase5-dev qtbase5-dev qt6-base-dev That agrees with the CMake gate on every distro this branch ships to. Same pair of fixes as PickNikRobotics/rviz_visual_tools#300. Co-Authored-By: Claude Opus 5 (1M context) --- visualization/CMakeLists.txt | 11 +++++++++-- visualization/package.xml | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/visualization/CMakeLists.txt b/visualization/CMakeLists.txt index 13578a3b1..b5c81c68c 100644 --- a/visualization/CMakeLists.txt +++ b/visualization/CMakeLists.txt @@ -19,9 +19,16 @@ find_package(rviz_ogre_vendor REQUIRED) add_definitions(-DBOOST_MATH_DISABLE_FLOAT128) # Qt Stuff -find_package(Qt6 QUIET COMPONENTS Core Widgets) -if(Qt6_FOUND) +# rviz switched to Qt6 in 15.1.14 (ros2/rviz#1635 merged 2025-12-08 but did not +# land until that patch release; 15.1.13 and earlier are still Qt5). Gate on +# rviz's version rather than Qt6 availability -- Ubuntu 24.04 ships both Qt5 and +# Qt6, and linking a Qt6 build against a Qt5 rviz is an ABI mismatch. +if(rviz_common_VERSION VERSION_GREATER_EQUAL 15.1.14) + find_package(Qt6 REQUIRED COMPONENTS Core Widgets) set(QT_VERSION_MAJOR 6) + # Hint for transitive deps that use find_package(QT NAMES Qt6 Qt5 ...) which + # may incorrectly resolve to Qt5 due to CMake's ascending path order + set(QT_DIR "${Qt6_DIR}") else() find_package(Qt5 REQUIRED COMPONENTS Core Widgets) set(QT_VERSION_MAJOR 5) diff --git a/visualization/package.xml b/visualization/package.xml index 6115fccaf..65092c882 100644 --- a/visualization/package.xml +++ b/visualization/package.xml @@ -11,7 +11,7 @@ ament_cmake fmt - qtbase5-dev + qt-base-dev moveit_core moveit_task_constructor_msgs moveit_task_constructor_core