Qt for Build Engineers

Qt from the build side: module structure, the moc/uic/rcc/qmlcachegen code generators and what they do to the build graph, qmake versus CMake in Qt 6, static builds, and LGPL as a build constraint.

Overview

Qt through a build engineer's eyes

Most Qt material is written for application developers. A build engineer needs a different model: Qt is a C++ framework with a mandatory code-generation phase, a plugin-based runtime that fails in a distinctive way when packaged wrong, and a dual-licensing model that constrains how you may link.

Code generation is the defining fact

Signals and slots, dynamic properties and runtime introspection are not compiler features. moc scans headers for Q_OBJECT and emits a C++ file with each class's meta-object data, which must be compiled and linked like any other TU.

> [!NOTE] The extra dependency edges, stale-build failures and the "undefined reference to vtable" error every Qt developer has seen all follow from this.

  • moc — Q_OBJECT classes — Meta-object C++
  • uic — Qt Designer .ui XML — A header with setupUi
  • rcc — .qrc manifest — Files embedded as a virtual filesystem
  • qmlcachegen — QML — Precompiled bytecode, no parse cost at startup

CMake automates them via CMAKE_AUTOMOC, CMAKE_AUTOUIC and CMAKE_AUTORCC, enabled by qt_standard_project_setup. Automation hides the dependency structure rather than removing it, so failures still need the model underneath.

Qt 6 made CMake primary

Qt itself builds with CMake, the docs lead with it, and Qt-specific commands (qt_add_executable, qt_add_library, qt_add_qml_module, qt_add_resources) do more than plain CMake, especially static plugin importing and mobile targets. Qt 5 codebases are still overwhelmingly qmake, so migration is a realistic interview topic.

Modules drive link time and footprint

> [!TIP] Knowing which modules a binary actually needs is the difference between a 40 MB deployment and a 200 MB one.

Licensing is a fair question

Under LGPLv3 you may keep application source proprietary with dynamic linking, but users must be able to change and relink the Qt library (including reverse engineering for that purpose) and run the modified binary. Static linking does not forbid LGPL use, but makes that obligation much harder, which is why "static Qt" and "commercial license" appear in the same conversation.

Key concepts

Q_OBJECT and moc
Q_OBJECT is a macro that marks a class for processing by the Meta-Object Compiler. moc reads headers, finds those classes, and generates a C++ file (moc_myclass.cpp) containing the QMetaObject that implements signals and slots, dynamic properties, and runtime type information. Standard C++ has no reflection, so Qt generates it at build time.
CMAKE_AUTOMOC / AUTOUIC / AUTORCC
CMake features that scan a target sources for Q_OBJECT and Q_GADGET, .ui files, and .qrc files respectively, run the corresponding generator, and add the output to the build. qt_standard_project_setup enables AUTOMOC. The critical limitation is that scanning only covers files actually listed in a target.
uic and rcc
uic converts a Qt Designer .ui XML file into ui_.h containing a setupUi function that builds the widget tree in code — no runtime XML parsing. rcc reads a .qrc manifest and compiles the listed files into the binary as a virtual filesystem reachable through paths starting with a colon.
qt_standard_project_setup
A Qt 6 CMake command that applies project-wide defaults: it sets CMAKE_AUTOMOC to ON, enables AUTOUIC for GUI projects, and configures C++ standard and output directory conventions. Calling it near the top of a Qt 6 CMakeLists.txt removes a page of boilerplate.
qt_add_executable
A wrapper around add_executable that adds Qt-specific handling for plugins and platform customisation. On Android, iOS, and WebAssembly it produces the platform-appropriate artifact rather than a bare executable, and in static builds it arranges for required plugins to be imported.
Namespaced Qt targets
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Network) provides imported targets Qt6::Core, Qt6::Widgets, Qt6::Network. Linking them with target_link_libraries propagates include directories, compile definitions, and transitive dependencies. Never hand-roll -lQt6Core or add Qt include paths manually.
Static versus shared Qt
Shared is the default: the application loads Qt DLLs or shared objects at runtime and requires a plugin directory. Static links Qt into the executable, eliminating deployment complexity and the platform-plugin failure mode, but requires building Qt from source with -static and creates an LGPLv3 relinking obligation that shared linking does not.
LGPLv3 relinking obligation
Under LGPLv3, the recipient must be able to change and relink the Qt library used in the application, including reverse engineering for that purpose, and must be able to run the modified result. Dynamic linking satisfies this naturally. You must also supply the complete corresponding source of the Qt version used, including any modifications, or a written offer for it.

When to use it

  • Owning the build for a desktop or embedded C++ application that uses Qt Widgets or Qt Quick for its interface
  • Migrating a Qt 5 qmake project to Qt 6 and CMake, where the implicit conventions of .pro files have to become explicit CMake
  • Diagnosing moc-related link failures — undefined vtables, missing meta-objects, or signals that connect at runtime and never fire
  • Deciding whether to consume a prebuilt Qt from the online installer or build Qt from source for a constrained or unusual target
  • Making the static-versus-shared and LGPL-versus-commercial decision, where the build topology and the licence obligation are the same conversation

Interview questions

  • Explain the moc pipeline end to end and what breaks when it goes wrong.
  • Write the CMakeLists.txt for a Qt 6 Widgets application and justify each command.
  • You inherit a Qt 5 qmake project and are asked to move it to Qt 6 with CMake. Plan it.
  • Static Qt versus shared Qt — walk through the build, deployment, and licensing consequences.
  • Which Qt modules does a given application actually need, and how do you find out?

Sign up free to read the full topic

More DevOps topics

Start free on Cariara · Coding problems · System design studio