Consumer packaging & CMake (1.46 · find_package sketch 1.61)¶
End-to-end path for a third-party app on Windows and Linux. Prefer types in stable-api.md. Compatibility freeze: compatibility-1xx.md · upgrades: upgrade-notes.md (1.40).
Open this monorepo in Qt Creator (Gallery / examples — CMake only, no .pro): qt-creator.md (1.35).
Consumers typically:
- Build from this repo (static or shared), or
- Package locally with
scripts/package_release_libs.py, or add_subdirectory/ clone the kit into their tree, orfind_package(QWinUI3 CONFIG)against a packaged tree — see Path C · vcpkg / Conan, or- Python (PySide6 / PyQt6) + shared kit — see Path E · packaging-python.md.
vcpkg / Conan (2.11): Official in-repo ports — packaging-vcpkg-conan.md. Zip + Path C remain valid; 2.02 still productizes
find_packageas the primary path without overlay.
1.46 polish: shared vs static matrix, windeploy/linuxdeploy notes, strip-restricted modules.
1.61 sketch: QWinUI3Config.cmake + examples/find-package-consumer + scripts/verify_find_package.py.
2.34 v2: consumer matrix (shared/static × Win/Linux) + CI job mapping below.
Path picker (2.47 / FL-003)¶
Use this table before copying the Gallery monorepo tree. Full find_package productize remains 2.02; vcpkg/Conan overlay is 2.11 (packaging-vcpkg-conan.md).
| Your situation | Path | Doc anchor |
|---|---|---|
| Learning / first Win11 shell app | D — add_subdirectory + examples/first-app/ or gallery-shell/ |
first-app-252.md |
| Python / PySide6 / PyQt6 app | E — shared kit + python/qwinui3/ |
packaging-python.md |
| Packaging from this repo locally | B — package_release_libs.py --shared |
Shared vs static |
| Corporate mirror / vcpkg / Conan | overlay | packaging-vcpkg-conan.md |
| Installed prefix + CMake config | C — find_package(QWinUI3 CONFIG) |
Path C |
2.47 harden: field-harden-247.md · Pitfalls FL-003 checklist · smoke loads RecipesHubPage.
Shared vs static¶
Shared (QWINUI3_BUILD_SHARED=ON / --shared) |
Static (default in-tree / Gallery) | |
|---|---|---|
| Artifact | bin/*.dll + lib/*.lib (Win) or lib/*.so* (Linux) + qml/ |
lib/*.lib / *.a + QML plugins + qml/ |
| Link | qwinui3_theme … only (import libs) |
Same plus qwinui3_*plugin targets |
| Runtime | Ship QWinUI3 DLLs/.so next to the app (or PATH / LD_LIBRARY_PATH / rpath) |
QWinUI3 code linked into your binary; still ship Qt runtime |
| When to use | Redistributable zip for third parties; multiple apps sharing one kit | Single app / Gallery-style; fewer loose files |
| Package command | python scripts/package_release_libs.py --shared --archive |
omit --shared |
Rule of thumb: build and package shared kits locally with package_release_libs.py --shared --archive. Gallery deploy uses package_release_gallery.py.
CMake dependency note (1.46): qwinui3_style and qwinui3_extras PUBLIC-link qwinui3_platform. Packaging presets core / style / extras therefore also collect the platform DLL/.so and QWinUI3/Platform QML (same runtime set as shell for style-based apps). Theme-only (--modules theme) stays the smallest shared kit.
Module presets (deps auto-included): all / full · core (theme+style, +platform) · shell · extras (theme+extras, +platform) · per-module names.
List: python scripts/package_release_libs.py --list-modules.
Shared builds set CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS so C++ helpers such as ThemeFonts::ensureLoaded (used from Bootstrap) cross DLL boundaries on MSVC.
Consumer matrix (2.34)¶
Maintainers and third-party integrators can map how you link QWinUI3 to what CI proves on every relevant PR:
| Windows | Linux | |
|---|---|---|
| Static (default in-tree) | Build qwinui3_example_gallery_shell — Path D proxy (add_subdirectory, link *plugin targets) |
same |
Shared (--shared / QWINUI3_BUILD_SHARED=ON) |
package_release_libs.py --shared --preset shell → verify_find_package.py (Path C) |
same |
| Workflow | When | Role |
|---|---|---|
.github/workflows/smoke.yml |
push / PR to master |
Gallery Release build + catalog/--smoke |
.github/workflows/consumer-matrix.yml |
packaging / examples / src/ changes |
Static + shared consumer on Win + Linux |
Run the shared-kit check locally after packaging:
python scripts/verify_find_package.py --package-dir dist/qwinui3-<ver>-<plat>-x64-shared-theme+style+platform --skip-package
Package layout (shared)¶
After extract (or python scripts/package_release_libs.py --shared --archive):
qwinui3-<ver>-…-shared/
bin/ # Windows: runtime DLLs (also copy beside your .exe or on PATH)
lib/ # Import libs (.lib) / shared objects (.so) / plugins
lib/cmake/QWinUI3/ # find_package sketch (1.61)
include/QWinUI3/ # Bootstrap.h when platform is packaged
qml/ # QML trees: QWinUI3/, QWinUI3/Theme, QWinUI3/Platform, QWinUI3/Extras
README.md
LICENSE · NOTICE
| Module | CMake target | QML under qml/ |
|---|---|---|
theme |
qwinui3_theme |
QWinUI3/Theme |
style |
qwinui3_style |
QWinUI3 (Controls style) |
platform |
qwinui3_platform |
QWinUI3/Platform |
extras |
qwinui3_extras |
QWinUI3/Extras |
Validate a tree with python scripts/verify_find_package.py --package-dir dist/qwinui3-<ver>-windows-x64-shared (or the Linux equivalent).
Path A — use a locally packaged shared kit¶
Build the kit from this repository, then extract the archive from dist/:
1. Extract and point at Qt¶
You still need a Qt 6.5+ install (recommended 6.8 matching CI) with Quick + QuickControls2 + LabsQmlModels.
2. Runtime (before QGuiApplication)¶
Prefer the one-call bootstrap from qwinui3_platform (Bootstrap.h):
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "Bootstrap.h"
QWINUI3_IMPORT_QML_PLUGINS
int main(int argc, char *argv[])
{
QWinUI3::configureEnvironment(argv[0]); // style env + platform QPA/DPI
QGuiApplication app(argc, argv);
QWinUI3::configureApplication(QStringLiteral("org.example.myapp"));
QQmlApplicationEngine engine;
// Absolute path to the package's qml/ folder:
engine.addImportPath(QStringLiteral("D:/deps/qwinui3-1.46-windows-x64-shared/qml"));
// …
}
Manual equivalent:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickStyle>
int main(int argc, char *argv[])
{
qputenv("QT_QUICK_CONTROLS_STYLE", "QWinUI3");
QGuiApplication app(argc, argv);
QQuickStyle::setStyle(QStringLiteral("QWinUI3"));
QQmlApplicationEngine engine;
// Absolute path to the package's qml/ folder:
engine.addImportPath(QStringLiteral("D:/deps/qwinui3-1.46-windows-x64-shared/qml"));
// …
}
Windows: ensure bin/*.dll (or copies of the DLLs) are next to the .exe or on PATH.
Linux: add lib/ to LD_LIBRARY_PATH, or embed rpath to that directory.
Env alternatives: QML_IMPORT_PATH / QML2_IMPORT_PATH including the package qml/ directory (same effect as addImportPath).
3. Minimal consumer CMakeLists.txt (shared package)¶
cmake_minimum_required(VERSION 3.21)
project(MyFluentApp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Extracted shared package root (contains bin/, lib/, qml/)
set(QWINUI3_ROOT "D:/deps/qwinui3-1.46-windows-x64-shared" CACHE PATH "QWinUI3 shared package")
find_package(Qt6 6.5 REQUIRED COMPONENTS Quick QuickControls2 LabsQmlModels Gui)
qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(myapp main.cpp)
qt_add_qml_module(myapp
URI MyApp
VERSION 1.0
QML_FILES Main.qml
)
target_link_directories(myapp PRIVATE "${QWINUI3_ROOT}/lib")
target_link_libraries(myapp PRIVATE
Qt6::Quick
Qt6::QuickControls2
qwinui3_theme
qwinui3_style
qwinui3_platform
qwinui3_extras
)
# Bake the QML import path for local runs (optional; also set at runtime)
target_compile_definitions(myapp PRIVATE
QWINUI3_QML_ROOT=u8"${QWINUI3_ROOT}/qml"
)
# Windows: copy runtime DLLs next to the exe after build (optional helper)
if(WIN32)
add_custom_command(TARGET myapp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${QWINUI3_ROOT}/bin" $<TARGET_FILE_DIR:myapp>
COMMENT "Copy QWinUI3 DLLs beside myapp"
)
endif()
In main.cpp, if you used the define:
Link only the modules you packaged (core → theme+style only). On Linux, library names are typically libqwinui3_theme.so — target_link_libraries(… qwinui3_theme) still works if lib/ is on the link path.
4. QML side¶
import QtQuick
import QtQuick.Controls
import QWinUI3.Theme
import QWinUI3.Extras
import QWinUI3.Platform
ApplicationWindow { /* or StandardWindow from Platform */ }
Keep QT_QUICK_CONTROLS_STYLE=QWinUI3 so styled Controls pick up the Fluent chrome.
Path C — find_package sketch (1.61)¶
Primary path (2.76): For new C++ consumers that already ship a CMake package prefix, prefer Path C —
find_package(QWinUI3 CONFIG)against a shared kit frompackage_release_libs.py --shared. Walkthrough: getting-started.md — Path C · runnable sketch:examples/find-package-consumer. Zip (Path B) andadd_subdirectory(Path A) remain valid; vcpkg/Conan stay on packaging-vcpkg-conan.md.
Experimental sketch (1.61). Same shared zip as Path A; Config files live under lib/cmake/QWinUI3/. vcpkg / Conan consumers: packaging-vcpkg-conan.md (Path D/E).
1. Package (or download) a shared kit¶
2. Consumer CMakeLists.txt¶
cmake_minimum_required(VERSION 3.21)
project(MyFluentApp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Qt + extracted package root (contains lib/cmake/QWinUI3)
list(APPEND CMAKE_PREFIX_PATH
"D:/Qt/6.8.0/msvc2022_64"
"D:/deps/qwinui3-1.61-windows-x64-shared-theme+style+platform"
)
find_package(Qt6 6.5 REQUIRED COMPONENTS Quick QuickControls2 LabsQmlModels Gui)
find_package(QWinUI3 CONFIG REQUIRED)
qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(myapp main.cpp)
qt_add_qml_module(myapp URI MyApp VERSION 1.0 QML_FILES Main.qml)
target_link_libraries(myapp PRIVATE
Qt6::Quick
Qt6::QuickControls2
QWinUI3::theme
QWinUI3::style
QWinUI3::platform
)
# Or: target_link_libraries(myapp PRIVATE QWinUI3::QWinUI3)
qwinui3_target_setup(myapp) # QWINUI3_QML_ROOT + copy bin/ DLLs on Windows
In main.cpp (shared kits — do not QWINUI3_IMPORT_QML_PLUGINS):
#include <QWinUI3/Bootstrap.h>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QWinUI3::configureEnvironment(argv[0]);
QGuiApplication app(argc, argv);
QWinUI3::configureApplication(QStringLiteral("org.example.myapp"));
QQmlApplicationEngine engine;
#ifdef QWINUI3_QML_ROOT
engine.addImportPath(QString::fromUtf8(QWINUI3_QML_ROOT));
#endif
engine.loadFromModule("MyApp", "Main");
return app.exec();
}
3. Tiny verified sample¶
| Path | Role |
|---|---|
examples/find-package-consumer/ |
Standalone app (not in monorepo example build) |
scripts/verify_find_package.py |
Package shell kit → configure → Release build |
python scripts/verify_find_package.py
python scripts/verify_find_package.py --package-dir dist/qwinui3-<ver>-windows-x64-shared-theme+style+platform
Legacy lowercase targets (qwinui3_theme, …) remain as aliases of QWinUI3::* for older Path A snippets.
Path B — package from this repo¶
REM Needs CMAKE_PREFIX_PATH / Qt6_DIR pointing at Qt 6.5+ (CI uses 6.8)
python scripts/package_release_libs.py --shared --archive
python scripts/package_release_libs.py --shared --preset shell --archive
python scripts/package_release_gallery.py
Output under dist/. Then follow Path A. Static packaging (--shared omitted) is for linking .lib/.a into your binary — you must also link the *plugin targets (see in-tree examples).
After packaging, run python scripts/verify_find_package.py --package-dir dist/qwinui3-<ver>-<plat>-x64-shared.
Path D — add_subdirectory (develop against source)¶
Best when you want CMake targets (qwinui3_theme, …) without a zip:
set(QWINUI3_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
# Default OFF = static into your app (same as Gallery). ON = shared DLLs/.so
# set(QWINUI3_BUILD_SHARED ON CACHE BOOL "" FORCE)
add_subdirectory(third_party/QWinui3)
target_link_libraries(myapp PRIVATE
Qt6::Quick Qt6::QuickControls2
qwinui3_theme qwinui3_style qwinui3_platform qwinui3_extras
)
if(NOT QWINUI3_BUILD_SHARED)
target_link_libraries(myapp PRIVATE
qwinui3_themeplugin qwinui3_styleplugin
qwinui3_platformplugin qwinui3_extrasplugin
)
endif()
Copy an example under examples/ — prefer gallery-shell (1.50) for app chrome, floating-osk (1.84) for OnScreenKeyboardWindow, multi-window (1.56) for secondary tool/dialog HWNDs, python-gallery (2.64) for PySide6 / PyQt6, or nav-settings / settings-cards / dashboard for specialized recipes — and keep the same IMPORTS / Q_IMPORT_QML_PLUGIN pattern as that example’s main.cpp.
SIL Keyman Core sources ship in the clone at third_party/keyman (NOTICE-Keyman.md). WebView2 remains an optional NuGet fetch (scripts/fetch_webview2.ps1) — not required for the OSK example.
Path E — Python (PySide6 / PyQt6) + shared kit¶
Shipped early on 2.64 (full Gallery; PyPI wheels remain 2.72).
pip install PySide6(orPyQt6+QWINUI3_QT_BINDING=pyqt6).- Package a shared kit:
python scripts/package_release_libs.py --shared --archive. - Match kit Qt major.minor to
from PySide6.QtCore import qVersion. - Run Gallery:
python scripts/qwinui3.py pythonorpython scripts/qwinui3.py python --smoke.
Recipe: packaging-python.md. Bootstrap: python/qwinui3/. Gallery: python/qwinui3_gallery/.
Import bindings before kit DLL directories (qwinui3.configure_environment()). Do not subprocess qwinui3_gallery.exe.
Deploying your app (windeployqt / linuxdeploy)¶
QWinUI3 shared zips do not include the Qt runtime. Your installer must still ship Qt (and any optional Multimedia / WebView2 pieces you enable). MediaPlayerElement stays experimental / deferred 1.67 — media.md; Multimedia plugins are never a kit promise.
Windows — windeployqt¶
- Build your app Release against the same Qt major/minor as the QWinUI3 package.
- Copy QWinUI3
bin/*.dllbeside the.exe(POST_BUILD above) or leave them onPATHfor local runs only. - Run
windeployqton the exe (Quick + QuickControls2). Example:
- Ensure
engine.addImportPath/QML_IMPORT_PATHstill sees the QWinUI3 packageqml/(or copy that tree under your deploy folder and point there). - Run strip-restricted cleanup so GPL/Commercial Qt add-ons do not ride along (see below).
Gallery reference: python scripts/package_release_gallery.py (calls windeployqt, then strips restricted modules).
Linux — linuxdeploy (+ qt plugin)¶
- Prefer rpath into the shared package
lib/, or setLD_LIBRARY_PATHfor the installed layout. - Use linuxdeploy +
linuxdeploy-plugin-qtto gather Qt libs/plugins/QML — same pattern as Gallery packaging. - Do not assume AppImage is required; a relocatable AppDir / tarball is enough for many LoB apps.
- Strip restricted Qt modules from the staged tree after deploy.
Gallery reference: same package_release_gallery.py path on Linux.
Strip-restricted Qt modules¶
QWinUI3 is Apache-2.0. See licensing.md. Desktop Qt kits / windeployqt / linuxdeploy-plugin-qt may copy GPL or commercial add-ons (notably Virtual Keyboard, Charts, WebEngine, Quick3D, …) into the deploy folder.
The in-app OSK (1.73) is QWinUI3 QML + SIL Keyman Core (MIT) for layouts, MIT pinyin-data for zh-Hans, romaji→kana for ja, and Unicode hangul for ko. It does not restore Qt Virtual Keyboard. See on-screen-keyboard.md, NOTICE-Keyman.md, and NOTICE-pinyin.md.
In this repo
| Helper | Use |
|---|---|
cmake/StripRestrictedQtModules.cmake → qwinui3_strip_restricted_qt_modules(target) |
POST_BUILD remove from $<TARGET_FILE_DIR:…> (Gallery + examples) |
scripts/package_release_gallery.py → _strip_restricted() |
Cleans staged Gallery zip / AppDir |
Consumer apps: call the CMake helper on your executable target, or delete the same relative paths after windeployqt / linuxdeploy. Restricted Qt add-on trees should never appear inside a QWinUI3 lib package.
Qt Creator (consumer app)¶
- Kit: Qt 6.5+ (6.8 recommended), MSVC 2022 x64 on Windows / gcc_64 on Linux.
- Set
QWINUI3_ROOTin the project’s CMake configuration (or hardcode while prototyping). - For Path C (
find_package) or Path D (add_subdirectory), open your app’sCMakeLists.txt(not necessarily the QWinUI3 root). - Run configuration: ensure
PATH/LD_LIBRARY_PATHandQML_IMPORT_PATHinclude the packagebin/lib/qmlas needed.
Opening the QWinUI3 monorepo itself: qt-creator.md.
Checklist¶
| Step | Windows | Linux |
|---|---|---|
| Qt prefix | CMAKE_PREFIX_PATH → …/msvc2022_64 |
…/gcc_64 |
| Library kind | Shared zip → copy bin/; Static → link *plugin |
Shared → lib/ on LD_LIBRARY_PATH / rpath |
| Style | QT_QUICK_CONTROLS_STYLE=QWinUI3 |
same |
| QML | engine.addImportPath(…/qml) |
same |
| Qt runtime | windeployqt (+ strip-restricted) |
linuxdeploy + qt plugin (+ strip) |
| Validate kit | verify_find_package.py --package-dir … |
same |
| API surface | Prefer stable-api.md | same |
| License | Apache-2.0 (LICENSE / NOTICE in package) |
same |
Smoke (maintainers)¶
# Static consumer (Path D proxy — matches consumer-matrix.yml)
cmake -S . -B build-static -G Ninja -DCMAKE_BUILD_TYPE=Release \
-DQWINUI3_BUILD_EXAMPLES=ON -DQWINUI3_BUILD_WEBVIEW2=OFF
cmake --build build-static --config Release --target qwinui3_example_gallery_shell --parallel
# Windows shared artifact + find_package sketch (1.61 / 2.34 shared cell)
python scripts/package_release_libs.py --shared --preset core --archive
python scripts/verify_find_package.py
python scripts/verify_find_package.py --package-dir dist/qwinui3-<ver>-windows-x64-shared-theme+style+platform
# Linux (local gcc_64 kit)
python scripts/package_release_libs.py --shared --archive
CI consumer-matrix runs static + shared cells on Win/Linux for packaging-related changes.
Out of scope¶
macOS packages, rewriting the CI module matrix, vendoring Qt through the port. 2.02 still makes Path C the primary documented find_package flow without overlay.