Table of Contents

  1. Qt5 vs Qt6 CMake Integration
  2. AUTOMOC, AUTOUIC & AUTORCC
  3. Finding Qt Components
  4. QML Resource Management
  5. Qt Deployment Tools
  6. Static Qt Builds
  7. Qt Creator vs CMake CLI
Back to CMake Mastery Series

Qt Framework

June 4, 2026 Wasil Zafar 14 min read

The complete guide to building Qt applications with CMake — from AUTOMOC magic to QML modules, deployment packaging, and the Qt5-to-Qt6 transition.

GUI

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)
Key Insight: Qt6 introduced 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
Pitfall — AUTOMOC and Include Paths: AUTOMOC scans for 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")
CMAKE_PREFIX_PATH: The most reliable way to locate Qt. Set it to the Qt installation's compiler-specific directory (e.g., 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
Pitfall — Missing Plugins: Qt GUI apps crash at startup with "This application failed to start because no Qt platform plugin could be initialized" if the 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
)
Licensing Note: Static linking with Qt requires either a commercial license or compliance with LGPL's "re-linking" requirement (providing object files so users can re-link against a modified Qt). Check your licensing obligations before distributing static Qt binaries.

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}"
            }
        }
    ]
}
Best Practices Summary:
  • Use qt_add_executable() instead of add_executable() for Qt6
  • Enable AUTOMOC/AUTOUIC/AUTORCC globally with qt_standard_project_setup()
  • Use qt_add_qml_module() for QML resources (not .qrc files)
  • Set CMAKE_PREFIX_PATH to your Qt installation for reliable discovery
  • Use CMake Presets to standardize Qt paths across team and CI
  • Always run deployment tools before distributing