CMakeLists: dual-support Qt5 and Qt6 - #300
Merged
Merged
Conversation
nbbrooks
force-pushed
the
nbbrooks/rolling-qt6-dual-support
branch
from
August 2, 2026 20:03
a517835 to
c7f702f
Compare
nbbrooks
force-pushed
the
nbbrooks/rolling-qt6-dual-support
branch
from
August 2, 2026 22:02
85207b8 to
92ddda6
Compare
nbbrooks
added a commit
that referenced
this pull request
Aug 2, 2026
Three problems, all of which make CI on `ros2` uninformative today. 1. fail-fast The matrix had no `fail-fast: false`, so it defaulted to true. The rolling job fails in ~60s (problem 2) and GitHub then cancels every sibling before it can report. From PR #300, run 30764760558 attempt 3: rolling-main + ccov 20:49:41 -> 20:50:45 failure humble-main 20:49:47 -> 20:50:51 cancelled humble-testing 20:49:47 -> 20:50:52 cancelled rolling-testing 20:49:41 -> 20:50:52 cancelled The matrix reported nothing at all, and re-running reproduced it exactly. 2. rolling + ROS_REPO: main on Resolute Rolling's base OS moved to Ubuntu Resolute. Rolling's `main` apt repo has no Resolute packages yet, so the job dies before CMake: 'sudo apt-get install ... ros-rolling-ros-environment' returned with 100 'setup_rosdep' returned with code '100' after 0 min 7 sec Not PR-specific: PR #297 fails the identical job. Switch it to `testing`, which does work on Resolute, and pin OS_CODE_NAME explicitly rather than relying on industrial_ci's default -- that default has changed before. Mark it non-blocking, matching the policy moveit2 applies to its own rolling-resolute job ("non-blocking until all Resolute packages are released"). CCOV rides on this job, so coverage is non-blocking too. 3. Three released distros had no CI at all This repo ships a single `ros2` branch to five distros but only tested two. jazzy (4.1.4-4), kilted (4.1.4-4) and lyrical (4.1.4-5) are all released from this branch with zero coverage. Unlike moveit2 -- which can omit distros from `main` because it maintains per-distro branches -- there is no second branch here to pick up the slack. That gap lands exactly on the case that matters most for the Qt work: jazzy and kilted are Ubuntu Noble, the platform where Qt5 and Qt6 can be installed side by side, which is precisely what the rviz-version Qt gate in CMakeLists guards against. humble covers jammy/Qt5 and rolling covers resolute/Qt6; the interesting middle was untested. lyrical is Resolute and released, so its `main` repo is populated -- it gives a blocking Qt6-on-Resolute job that should stay green, instead of leaving all Qt6 coverage on the non-blocking rolling job. Restore the rolling+main entry once Rolling publishes to `main` for Resolute, and drop the NONBLOCKING flag at the same time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
nbbrooks
added a commit
that referenced
this pull request
Aug 2, 2026
Three problems, all of which make CI on `ros2` uninformative today. 1. fail-fast The matrix had no `fail-fast: false`, so it defaulted to true. The rolling job fails in ~60s (problem 2) and GitHub then cancels every sibling before it can report. From PR #300, run 30764760558 attempt 3: rolling-main + ccov 20:49:41 -> 20:50:45 failure humble-main 20:49:47 -> 20:50:51 cancelled humble-testing 20:49:47 -> 20:50:52 cancelled rolling-testing 20:49:41 -> 20:50:52 cancelled The matrix reported nothing at all, and re-running reproduced it exactly. 2. rolling + ROS_REPO: main on Resolute Rolling's base OS moved to Ubuntu Resolute. Rolling's `main` apt repo has no Resolute packages yet, so the job dies before CMake: 'sudo apt-get install ... ros-rolling-ros-environment' returned with 100 'setup_rosdep' returned with code '100' after 0 min 7 sec Not PR-specific: PR #297 fails the identical job. Switch it to `testing`, which does work on Resolute, and pin OS_CODE_NAME explicitly rather than relying on industrial_ci's default -- that default has changed before. Mark it non-blocking, matching the policy moveit2 applies to its own rolling-resolute job ("non-blocking until all Resolute packages are released"). CCOV rides on this job, so coverage is non-blocking too. 3. Three released distros had no CI at all This repo ships a single `ros2` branch to five distros but only tested two. jazzy (4.1.4-4), kilted (4.1.4-4) and lyrical (4.1.4-5) are all released from this branch with zero coverage. Unlike moveit2 -- which can omit distros from `main` because it maintains per-distro branches -- there is no second branch here to pick up the slack. That gap lands exactly on the case that matters most for the Qt work: jazzy and kilted are Ubuntu Noble, the platform where Qt5 and Qt6 can be installed side by side, which is precisely what the rviz-version Qt gate in CMakeLists guards against. humble covers jammy/Qt5 and rolling covers resolute/Qt6; the interesting middle was untested. lyrical is Resolute and released, so its `main` repo is populated -- it gives a blocking Qt6-on-Resolute job that should stay green, instead of leaving all Qt6 coverage on the non-blocking rolling job. Restore the rolling+main entry once Rolling publishes to `main` for Resolute, and drop the NONBLOCKING flag at the same time. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Select Qt6 when building against rviz >= 15.1.14, which is the release where rviz itself switched to Qt6; otherwise keep Qt5. On Rolling / Ubuntu Resolute, rviz is built against Qt6, so this package's unconditional find_package(Qt5 REQUIRED COMPONENTS Widgets) collides with it and CMake refuses to generate at all: CMake Error: The INTERFACE_QT_MAJOR_VERSION property of "Qt5::Core" does not agree with the value of QT_MAJOR_VERSION already determined CMake Generate step failed. Build files cannot be regenerated correctly. Note the problem is the Qt5/Qt6 *mix*, not Qt5 being unavailable: Qt5 is still perfectly installable on Resolute (CI shows apt-get install qtbase5-dev pulling qt5-qmake-bin 5.15.18+dfsg-1ubuntu1). Availability is therefore the wrong thing to gate on -- see below. The Qt6 switch is ros2/rviz#1635 "Use qt6 as the default dependency from rosdep" (ahcorde, merged 2025-12-08, commit 4784c8cd), which flipped rviz_common's package.xml from qtbase5-dev/libqt5-* to qt6-base-dev/libqt6-*. It first shipped in rviz 15.1.14 (tagged 2025-12-17) -- note 15.1.0 through 15.1.13 are still Qt5, so the threshold is 15.1.14 and not 15.1. Gate on rviz_common_VERSION rather than on whether Qt6 is installed. An availability probe (find_package(Qt6 QUIET)) says nothing about which Qt rviz was built against, and rviz_visual_tools_gui links rviz_common, rviz_rendering, rviz_default_plugins and rviz_ogre_vendor::OgreMain, so its Qt must match rviz's. On a jazzy or kilted machine that also has qt6-base-dev installed, the availability 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. Targets Defined: Qt::Core Targets not yet defined: Qt::CorePrivate 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. Validated in osrf/ros:<distro>-desktop containers, comparing the gate's choice against objdump of librviz_common.so: distro rviz_common gate rviz links humble 11.2.26 Qt5 libQt5Core jazzy 14.1.22 Qt5 libQt5Core kilted 15.0.13 Qt5 libQt5Core lyrical 15.2.4 Qt6 libQt6Core rolling 16.0.1 Qt6 libQt6Core Also verified that with qt6-base-dev additionally installed on jazzy and kilted, this gate still selects Qt5 and configures cleanly. Mirrors the approach taken in moveit/moveit2#3707, including the QT_DIR hint for transitive deps whose find_package(QT NAMES Qt6 Qt5 ...) can mis-resolve to Qt5 under CMake's ascending path order. No source-side Qt6 changes are needed here: this package has no .ui files and none of the Qt5-only API patterns moveit2 had to fix -- only plain widget includes, Q_OBJECT, QCursor and Qt::Key_N enums, all unchanged in Qt6. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The CMake change alone is not enough. package.xml still hard-coded Qt5 rosdep keys: <build_depend>qtbase5-dev</build_depend> <exec_depend>libqt5-widgets</exec_depend> Those resolve to Qt5 on every platform, so on Resolute rosdep installs Qt5 while CMake needs Qt6 to match rviz -- which is exactly how the INTERFACE_QT_MAJOR_VERSION collision fixed in the previous commit arises. CI confirms the sequence: apt-get install qtbase5-dev and libqt5widgets5t64 both succeed on ubuntu:resolute, and the build then fails at generate. (To be clear, Qt5 has not been removed from Resolute's apt indexes; it installs fine. It simply must not be mixed with the Qt6 rviz we link.) Switch to the versionless keys, which rosdep maps per-platform. This is the same move ros2/rviz#1635 made in rviz_common's package.xml: key jammy noble * (resolute) qt-base-dev qtbase5-dev qtbase5-dev qt6-base-dev libqtwidgets libqt5widgets5 libqt5widgets5t64 libqt6widgets6 That mapping agrees with the CMake gate on all five distros, so rosdep installs exactly the Qt that CMake then selects: humble (jammy) rviz 11.2 -> Qt5 jazzy (noble) rviz 14.1 -> Qt5 kilted (noble) rviz 15.0 -> Qt5 lyrical (resolute) rviz 15.2 -> Qt6 rolling (resolute) rviz 16.0 -> Qt6 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
nbbrooks
force-pushed
the
nbbrooks/rolling-qt6-dual-support
branch
from
August 2, 2026 22:22
92ddda6 to
95b28d1
Compare
Member
Author
|
CI error predates this change. |
nbbrooks
added a commit
that referenced
this pull request
Aug 2, 2026
tf2_ros::TransformBroadcaster used to accept a node pointer through a set of
constructor overloads marked
[[deprecated("Use rclcpp::node_interfaces::NodeInterfaces instead of NodeT")]]
guarded by std::enable_if_t<rcpputils::is_pointer<NodeT>::value, bool>.
ros2/geometry2#940 "Removed deprecated code" (ahcorde, merged 2026-07-01,
+0/-978 across 13 files) deleted them. What remains takes
rclcpp::node_interfaces::NodeInterfaces<NodeParametersInterface,
NodeTopicsInterface>, which calls get_node_*_interface() directly on whatever
it is handed -- so a std::shared_ptr<rclcpp::Node> no longer satisfies it:
/opt/ros/rolling/include/rclcpp/rclcpp/node_interfaces/node_topics_interface.hpp:99:1:
error: 'const class std::shared_ptr<rclcpp::Node>' has no member named
'get_node_topics_interface'
99 | RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(
rclcpp::node_interfaces::NodeTopicsInterface, topics)
Dereference instead. The remaining constructor is templated on node type and
accepts a node reference on every supported distro, so no version guard is
needed -- and this is the migration the deprecation notice asked for.
First shipped in tf2_ros 0.46.2:
0.46.1 pointer ctor present -> old form compiles
0.46.2 removed by #940 -> old form fails
0.46.3 removed -> old form fails
Verified by compiling both forms against each distro's real headers:
distro (node) (*node)
humble ok ok
jazzy ok ok
rolling osrf/ros:rolling-desktop ok ok (tf2_ros 0.46.1)
rolling ros-testing on resolute FAILS ok (tf2_ros 0.46.3)
Note the prebuilt rolling desktop image still ships 0.46.1 and therefore
still accepts the old form; only the current ros-testing packages on
Resolute reject it, which is what CI builds against.
Surfaced by the rolling-testing job on #300 once #301 stopped fail-fast
from cancelling it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
nbbrooks
added a commit
that referenced
this pull request
Aug 2, 2026
tf2_ros::TransformBroadcaster used to accept a node pointer through a set of
constructor overloads marked
[[deprecated("Use rclcpp::node_interfaces::NodeInterfaces instead of NodeT")]]
guarded by std::enable_if_t<rcpputils::is_pointer<NodeT>::value, bool>.
ros2/geometry2#940 "Removed deprecated code" (ahcorde, merged 2026-07-01,
+0/-978 across 13 files) deleted them. What remains takes
rclcpp::node_interfaces::NodeInterfaces<NodeParametersInterface,
NodeTopicsInterface>, which calls get_node_*_interface() directly on whatever
it is handed -- so a std::shared_ptr<rclcpp::Node> no longer satisfies it:
/opt/ros/rolling/include/rclcpp/rclcpp/node_interfaces/node_topics_interface.hpp:99:1:
error: 'const class std::shared_ptr<rclcpp::Node>' has no member named
'get_node_topics_interface'
99 | RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(
rclcpp::node_interfaces::NodeTopicsInterface, topics)
Dereference instead. The remaining constructor is templated on node type and
accepts a node reference on every supported distro, so no version guard is
needed -- and this is the migration the deprecation notice asked for.
First shipped in tf2_ros 0.46.2:
0.46.1 pointer ctor present -> old form compiles
0.46.2 removed by #940 -> old form fails
0.46.3 removed -> old form fails
Verified by compiling both forms against each distro's real headers:
distro (node) (*node)
humble ok ok
jazzy ok ok
rolling osrf/ros:rolling-desktop ok ok (tf2_ros 0.46.1)
rolling ros-testing on resolute FAILS ok (tf2_ros 0.46.3)
Note the prebuilt rolling desktop image still ships 0.46.1 and therefore
still accepts the old form; only the current ros-testing packages on
Resolute reject it, which is what CI builds against.
Surfaced by the rolling-testing job on #300 once #301 stopped fail-fast
from cancelling it.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
On Rolling/Resolute, rviz is built against Qt6.
rviz_visual_tools'sCMakeLists.txtcallsfind_package(Qt5 REQUIRED COMPONENTS Widgets)unconditionally, so the two collide and CMake refuses to generate:That's from a real
rolling-testingrun onubuntu:resolute(#301, jobrolling-testing + ccov). Everything transitively depending on this package —moveit_visual_tools,moveit_task_constructor_visualization,snowbot_operating_system— is blocked behind it.This is the last Rolling-compat blocker for this repo. The other pieces an earlier audit flagged —
ament_target_dependenciesmigration (#277),tf2.h→.hpprenames (#284), CMake required-version bump (#287) — have all already landed onros2.How
Select Qt6 when building against rviz >= 15.1.14, otherwise keep Qt5.
That's the release where rviz itself switched: ros2/rviz#1635 "Use qt6 as the default dependency from rosdep" (ahcorde, merged 2025-12-08,
4784c8cd) flippedrviz_common/package.xmlfromqtbase5-dev/libqt5-*toqt6-base-dev/libqt6-*, and first shipped in the 15.1.14 tag on 2025-12-17.Note the threshold is 15.1.14, not 15.1 — bisecting upstream
rviz_common/package.xmlshows 15.1.0 through 15.1.13 are still Qt5:qtbase5-devqtbase5-devqtbase5-devqt6-base-devWhy gate on rviz's version rather than on Qt6 availability
An availability probe (
find_package(Qt6 QUIET)→if(Qt6_FOUND)) says nothing about which Qt rviz was built against.rviz_visual_tools_guilinksrviz_common,rviz_rendering,rviz_default_pluginsandrviz_ogre_vendor::OgreMain, so its Qt has to match rviz's.Ubuntu 24.04 ships both Qt5 and Qt6. On a jazzy or kilted machine that also has
qt6-base-devinstalled, the availability probe doesn't quietly mis-select — it aborts the configure, becauserviz_common(Qt5) has already defined the versionlessQt::Coretarget:QUIETdoes not suppress that — it's aFATAL_ERRORinside Qt6's own config, not a not-found condition, so the Qt5 fallback is never reached.This mirrors the approach in moveit/moveit2#3707, including the
QT_DIRhint for transitive deps whosefind_package(QT NAMES Qt6 Qt5 ...)can mis-resolve to Qt5 under CMake's ascending path order.Validation
Probed
osrf/ros:<distro>-desktopcontainers, comparing the gate's choice againstobjdump -p librviz_common.so:rviz_common_VERSIONlibQt5CorelibQt5CorelibQt5CorelibQt6CorelibQt6CoreAlso verified that with
qt6-base-devadditionally installed on jazzy and kilted, this gate still selects Qt5 and configures cleanly.package.xml: versionless Qt rosdep keys
The CMake gate alone is not sufficient.
package.xmlhard-coded Qt5 rosdep keys:Those resolve to Qt5 on every platform, so on Resolute rosdep installs Qt5 while CMake needs Qt6 — which is precisely how the
INTERFACE_QT_MAJOR_VERSIONcollision above arises. (Confirmed in CI:apt-get install qtbase5-devandlibqt5widgets5t64both succeed on Resolute, then the build fails at generate.)Switched to the versionless keys, the same move ros2/rviz#1635 made in
rviz_common's ownpackage.xml. rosdep maps them per-platform:*(resolute)qt-base-devqtbase5-devqtbase5-devqt6-base-devlibqtwidgetslibqt5widgets5libqt5widgets5t64libqt6widgets6Which agrees with the CMake gate on all five distros — rosdep installs exactly the Qt that CMake then selects:
Scope
Build metadata only —
CMakeLists.txt+package.xml. No C++ source changes are needed: this package has no.uifiles (so noqt5_wrap_ui) and none of the Qt5-only API patterns moveit2 had to fix — only plain widget includes,Q_OBJECT,QCursorandQt::Key_Nenums, all unchanged in Qt6.Behavior on humble/jazzy/kilted is unchanged.
CI caveat
This repo's
ros2matrix currently reports nothing: it lacksfail-fast: false, and itsrolling+ROS_REPO: mainjob cannot run on Resolute, so it fails in ~60s and cancels every sibling. #301 fixes that. This PR should be rebased on #301 before merging, so its Qt change actually gets validated rather than cancelled.Related
Overlaps #282 (competing Qt6 PR, 8 files, drops the Qt5 fallback and bumps C++ standard). This PR is intentionally minimal and preserves Qt5 for the distros that still need it; #288 reports #282 still doesn't build Qt6 cleanly. Suggest closing #282 in favor of this, and rebasing the CI-actions bump it carries onto
ros2separately if still wanted.🤖 Generated with Claude Code