Qt5 vs Qt6 CMake Integration
Qt made CMake its official build system starting with Qt6. While Qt5 supported CMake alongside qmake, Qt6 is CMake-first — meaning all new Qt APIs and best practices target CMake natively. The key differences between Qt5 and Qt6 CMake usage:
# Qt5 approach
find_package(Qt5 REQUIRED COMPONENTS Widgets Core Gui)
target_link_libraries(myapp PRIVATE Qt5::Widgets Qt5::Core Qt5::Gui)
# Qt6 approach (preferred for new projects)
find_package(Qt6 REQUIRED COMPONENTS Widgets Core Gui)
target_link_libraries(myapp PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui)
# Version-agnostic approach (supports both Qt5 and Qt6)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
target_link_libraries(myapp PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
qt_add_executable() which replaces add_executable() for Qt apps. It automatically handles platform-specific entry points (WinMain on Windows, bundle info on macOS) and enables finalization for static plugin linking.
# Modern Qt6 project setup
cmake_minimum_required(VERSION 3.21)
project(MyQtApp VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 REQUIRED COMPONENTS Widgets)
qt_standard_project_setup()
qt_add_executable(myapp
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
)
target_link_libraries(myapp PRIVATE Qt6::Widgets)
# Install rules
install(TARGETS myapp
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
AUTOMOC, AUTOUIC & AUTORCC
Qt's meta-object system requires special preprocessing — the Meta-Object Compiler (moc) processes headers with Q_OBJECT, the User Interface Compiler (uic) transforms .ui files, and the Resource Compiler (rcc) handles .qrc resource files. CMake automates all three:
# Enable Qt preprocessing globally
set(CMAKE_AUTOMOC ON) # Process Q_OBJECT/Q_GADGET macros
set(CMAKE_AUTOUIC ON) # Process .ui designer files
set(CMAKE_AUTORCC ON) # Process .qrc resource files
# Or per-target (preferred for libraries)
set_target_properties(mylib PROPERTIES
AUTOMOC ON
AUTOUIC ON
AUTORCC ON
)
Understanding what each auto-processor does:
// mainwindow.h — Q_OBJECT triggers AUTOMOC
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow {
Q_OBJECT // AUTOMOC detects this and runs moc
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals:
void dataChanged(const QString &value);
private slots:
void onButtonClicked();
private:
Ui::MainWindow *ui; // AUTOUIC processes mainwindow.ui
};
#endif
Q_OBJECT in headers listed as sources or found via include directories. If moc output is "not found", ensure the header with Q_OBJECT is either listed in target_sources() or the directory is in the target's include paths.
# Resources file (resources.qrc) compiled by AUTORCC
qt_add_executable(myapp
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui # Processed by AUTOUIC
resources.qrc # Processed by AUTORCC
)
Finding Qt Components
Qt is modular — you only link what you need. Common components and their purposes:
# Comprehensive Qt6 component discovery
find_package(Qt6 REQUIRED COMPONENTS
Core # QString, QObject, signals/slots, containers
Gui # QPainter, QImage, window system integration
Widgets # QPushButton, QMainWindow, classic desktop UI
Qml # QML engine
Quick # Qt Quick scene graph, visual items
QuickControls2 # Material/Universal styled controls
Network # HTTP, TCP, UDP, SSL
Sql # Database abstraction (SQLite, PostgreSQL, etc.)
Concurrent # QtConcurrent::run, thread pool
WebEngineWidgets # Chromium-based web view
Multimedia # Audio/video playback and recording
Svg # SVG rendering
)
# Specify Qt installation path if not in PATH
set(CMAKE_PREFIX_PATH "C:/Qt/6.7.0/msvc2022_64" CACHE PATH "Qt prefix")
# Or on Linux/macOS:
# set(CMAKE_PREFIX_PATH "$ENV{HOME}/Qt/6.7.0/gcc_64")
C:/Qt/6.7.0/msvc2022_64). This path contains the lib/cmake/ directory with Qt's config files.
QML Resource Management
Qt6 introduced qt_add_qml_module() as the modern way to manage QML files, replacing the older .qrc approach for QML resources:
# Qt6 QML Module (modern approach)
cmake_minimum_required(VERSION 3.21)
project(QmlApp LANGUAGES CXX)
find_package(Qt6 REQUIRED COMPONENTS Quick QuickControls2)
qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(qmlapp main.cpp)
qt_add_qml_module(qmlapp
URI MyApp
VERSION 1.0
QML_FILES
Main.qml
components/Header.qml
components/Sidebar.qml
pages/HomePage.qml
pages/SettingsPage.qml
RESOURCES
images/logo.png
images/icons/menu.svg
SOURCES
backend.h backend.cpp
)
target_link_libraries(qmlapp PRIVATE
Qt6::Quick
Qt6::QuickControls2
)
// main.cpp — Qt Quick application entry point
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(u"qrc:/MyApp/Main.qml"_qs);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
&app, []() { QCoreApplication::exit(-1); },
Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
Qt Deployment Tools
Qt applications require runtime libraries and plugins. CMake integrates with Qt's deployment tools to bundle everything needed:
# Deployment configuration in CMakeLists.txt
qt_generate_deploy_app_script(
TARGET myapp
OUTPUT_SCRIPT deploy_script
NO_UNSUPPORTED_PLATFORM_ERROR
)
install(SCRIPT ${deploy_script})
# Manual deployment commands per platform
# Windows — copies Qt DLLs, plugins, and platform plugins
windeployqt --release --no-translations build/Release/myapp.exe
# macOS — creates self-contained .app bundle
macdeployqt build/myapp.app -dmg -always-overwrite
# Linux — use linuxdeployqt (community tool)
linuxdeployqt build/myapp -appimage -bundle-non-qt-libs
platforms/ plugin directory isn't deployed alongside the executable. Always run the deploy tool or manually copy qwindows.dll / libqxcb.so.
Static Qt Builds
Static Qt produces a single executable with no external Qt dependencies — ideal for embedded systems and controlled deployments:
# Building Qt6 statically from source
git clone https://code.qt.io/qt/qt5.git qt6
cd qt6
git checkout v6.7.0
perl init-repository
mkdir build && cd build
../configure -static -release -prefix /opt/qt6-static \
-skip qtwebengine -skip qt3d \
-nomake examples -nomake tests \
-opensource -confirm-license
cmake --build . --parallel 8
cmake --install .
# Using static Qt in your project
set(CMAKE_PREFIX_PATH "/opt/qt6-static")
find_package(Qt6 REQUIRED COMPONENTS Widgets)
qt_add_executable(myapp main.cpp mainwindow.cpp)
target_link_libraries(myapp PRIVATE Qt6::Widgets)
# Static Qt requires importing plugins manually
qt_import_plugins(myapp
INCLUDE Qt::QWindowsIntegrationPlugin
INCLUDE Qt::QSvgPlugin
)
Qt Creator vs CMake CLI
Qt Creator and command-line CMake are complementary workflows. Qt Creator uses CMake under the hood but adds IDE features:
# Command-line workflow (CI-friendly)
cmake -B build -G Ninja \
-DCMAKE_PREFIX_PATH=/opt/Qt/6.7.0/gcc_64 \
-DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
cmake --install build --prefix /opt/myapp
# CMake Presets work in both Qt Creator and CLI
cmake --preset=release
cmake --build --preset=release
# CMakePresets.json — works in Qt Creator and CLI
{
"version": 6,
"configurePresets": [
{
"name": "qt6-debug",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build-debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_PREFIX_PATH": "$env{QT_DIR}"
}
},
{
"name": "qt6-release",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build-release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_PREFIX_PATH": "$env{QT_DIR}"
}
}
]
}
- Use
qt_add_executable()instead ofadd_executable()for Qt6 - Enable AUTOMOC/AUTOUIC/AUTORCC globally with
qt_standard_project_setup() - Use
qt_add_qml_module()for QML resources (not.qrcfiles) - Set
CMAKE_PREFIX_PATHto your Qt installation for reliable discovery - Use CMake Presets to standardize Qt paths across team and CI
- Always run deployment tools before distributing