Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qtqml-qml-type-compiler.qdoc
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
3
4/*!
5\page qtqml-qml-type-compiler.html
6\title QML type compiler
7\brief A tool to compile QML types to C++ ahead of time.
8\keyword qmltc
9
10The QML type compiler, \c qmltc, is a tool shipped with Qt to translate QML
11types into C++ types that are \e{ahead-of-time} compiled as part of the user
12code. Using qmltc can lead to better run-time performance due to more
13optimization opportunities available to the compiler compared to a
14QQmlComponent-based object creation. The qmltc is part of the \l{Qt Quick Compiler}
15toolchain.
16
17By design, qmltc outputs user-facing code. That code is supposed to be utilized
18by the C++ application directly, otherwise you won't see any benefit. This
19generated code essentially replaces QQmlComponent and its APIs to create objects
20from QML documents. You can find more information under \l{Using qmltc in a QML
21application} and \l{Generated Output Basics}.
22
23In order to enable qmltc:
24
25\list
26 \li Create a \l{qt_add_qml_module}{proper QML module} for your application.
27
28 \li Invoke qmltc, for example, through the \l{qmltc-cmake}{CMake API}.
29
30 \li \c{#include} the generated header file(s) in the application source
31 code.
32
33 \li Instantiate an object of the generated type.
34\endlist
35
36In this workflow qmltc usually runs during the build process. Thus, when qmltc
37rejects a QML document (whether due to errors or warnings, or because of
38constructs qmltc doesn't yet support), the build process will fail. This is
39similar to how you receive qmllint errors when you enable the automatic
40generation of linting targets during \l{qt_add_qml_module}{QML module creation}
41and then attempt to "build" them to run the qmllint.
42
43\warning qmltc is currently in a Tech Preview stage and might not compile an
44arbitrary QML program (see \l{Known Limitations} for more details). When qmltc
45fails, nothing is generated as your application cannot sensibly use the qmltc
46output. If your program contains errors (or unsolvable warnings), they should be
47fixed to enable the compilation. The general rule is to adhere to the best
48practices and follow \l{qmllint} advice.
49
50\note \c qmltc does not guarantee that the generated C++ stays API-, source- or
51binary-compatible between past or future versions, even patch versions.
52Furthermore, qmltc-compiled apps using Qt's QML modules will require linking
53against private Qt API. This is because Qt's QML modules do not usually provide
54a public C++ API since their primary usage is through QML.
55
56
57\section2 Using qmltc in a QML application
58
59From the build system perspective, adding qmltc compilation is not much
60different from adding qml cache generation. Naively, the build process could be
61described as:
62
63\image qmltc-compilation-scheme.png
64
65While the real compilation process is much trickier, this diagram captures the
66core components that qmltc uses: the QML files themselves and qmldir with
67qmltypes information. Simpler applications typically have rather primitive
68qmldir yet, in general, qmldir could be complex, providing essential, nicely
69packed type information that qmltc relies on to perform correct QML-to-C++
70translation.
71
72Nevertheless, adding an extra build step is not enough in qmltc case. The
73application code must also be modified to use qmltc-generated classes instead of
74QQmlComponent or its higher-level alternatives.
75
76\section3 Compiling QML code with qmltc
77
78Qt, starting from Qt 6, uses CMake to build its various components. User
79projects can - and are encouraged to - also use CMake to build their components
80using Qt. Adding out-of-the-box qmltc compilation support to your project would
81require a CMake-driven build flow as well since this flow is centered around
82proper QML modules and their infrastructure.
83
84The easy way to add qmltc compilation is by using the dedicated
85\l{qmltc-cmake}{CMake API} as part of a QML module creation for the application.
86Consider a simple application directory structure:
87
88\badcode
89.
90├── CMakeLists.txt
91├── myspecialtype.h // C++ type exposed to QML
92├── myspecialtype.cpp
93├── myApp.qml // main QML page
94├── MyButton.qml // custom UI button
95├── MySlider.qml // custom UI slider
96└── main.cpp // main C++ application file
97\endcode
98
99Then the CMake code would usually look similar to the following:
100
101\snippet qmltc/CMakeLists.txt qmltc-app-name
102\codeline
103\snippet qmltc/CMakeLists.txt qmltc-qml-files
104\codeline
105\snippet qmltc/CMakeLists.txt qmltc-add-qml-module
106\codeline
107\snippet qmltc/CMakeLists.txt qmltc-compile-to-cpp
108
109\section3 Using the Generated C++
110
111Unlike in the case of QQmlComponent instantiation, the output of qmltc, being
112C++ code, is used directly by the application. Generally, constructing a new
113object in C++ is equivalent to creating a new object through
114QQmlComponent::create(). Once created, the object could be manipulated from C++
115or, for example, combined with QQuickWindow to be drawn on screen. Given a
116\c{myApp.qml} file, the application code (in both cases) would typically look
117like this:
118
119\if defined(onlinedocs)
120 \tab {generated-c++}{tab-qqmlcomponent}{Using QQmlComponent}{checked}
121 \tab {generated-c++}{tab-qmltc}{Using qmltc-generated class}{}
122 \tabcontent {tab-qqmlcomponent}
123\else
124 \section4 Using QQmlComponent
125\endif
126\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-include
127\codeline
128\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-0
129\codeline
130\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-1
131\codeline
132\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-2
133\codeline
134\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-exec
135\if defined(onlinedocs)
136 \endtabcontent
137 \tabcontent {tab-qmltc}
138\else
139 \section4 Using qmltc-generated class
140\endif
141\snippet qmltc/tst_qmltc_examples.cpp qmltc-include
142\codeline
143\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-code
144\codeline
145\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-exec
146\if defined(onlinedocs)
147 \endtabcontent
148\endif
149
150\section2 QML engine
151
152The generated code uses QQmlEngine to interact with dynamic parts of a QML
153document - mainly the JavaScript code. For this to work, no special arrangements
154are needed. Any QQmlEngine instance passed to the constructor of a
155qmltc-generated class object should work correctly as does
156\c{QQmlComponent(engine)}. This also means that you can use
157\l{QQmlEngine}{QQmlEngine methods} that affect QML behavior. However, there are
158caveats. Unlike QQmlComponent-based object creation, qmltc itself \e{does not}
159rely on QQmlEngine when compiling the code to C++. For instance,
160\c{QQmlEngine::addImportPath("/foo/bar/")} - normally resulting in an additional
161import path to scan for - would be completely ignored by the ahead-of-time qmltc
162procedure.
163
164\note To add import paths to the qmltc compilation, consider using a relevant
165argument of the \l{qmltc-cmake}{CMake command} instead.
166
167Generally, you can think of it this way: QQmlEngine involves the application
168process to run, while qmltc does not as it operates \e{before} your application
169is even compiled. Since qmltc makes no attempt to introspect your application's
170C++ source code, there is no way for it to know about certain kinds of QML
171manipulations you, as a user, do. Instead of using QQmlEngine and related
172run-time routines to expose types to QML, adding import paths, etc. you are,
173practically, required to create \l{qt_add_qml_module}{well-behaving QML modules}
174and use \l{Defining QML Types from C++}{declarative QML type registration}.
175
176\warning Despite qmltc working closely with QQmlEngine and creating C++ code,
177the generated classes cannot be further exposed to QML and used through
178QQmlComponent.
179
180\section2 Generated Output Basics
181
182\c qmltc aims to be compatible with the existing QML execution model. This
183implies that the generated code is roughly equivalent to the internal
184QQmlComponent setup logic and thus you should be able to understand your QML
185type's behavior, semantics and API the same way you do currently - by visually
186inspecting the corresponding QML document.
187
188However, the generated code is still somewhat confusing, especially given that
189your application should use the qmltc output on the C++ side directly. There are
190two parts of the generated code: CMake build files structure and the generated
191C++ format. The former is covered in the \l{qmltc-cmake}{CMake API of qmltc} and
192the latter is covered here.
193
194Consider a simple HelloWorld type, that has a \c hello property, a function to
195print that property, and a signal emitted when the object of that type is
196created:
197
198\snippet qmltc/special/HelloWorld.qml qmltc-hello-world-qml
199
200When providing a C++ alternative of this QML type, the C++ class would need a
201\l{Overview - QML and C++ Integration}{QML-specific meta-object system macro},
202Q_PROPERTY decoration for the \c hello property, \c{Q_INVOKABLE} C++ printing
203function and a regular Qt signal definition. Similarly, qmltc would translate
204the given HelloWorld type into roughly the following:
205
206\snippet qmltc/special/HelloWorld.qml.cpp qmltc-hello-world-generated
207
208Even though specific details of the generated type could differ, the universal
209aspects remain. For instance:
210
211\list
212 \li QML types within a document are translated into C++ types, according to
213 the compiler-visible information.
214 \li Properties are translated into C++ properties with Q_PROPERTY
215 declarations.
216 \li JavaScript functions become \c{Q_INVOKABLE} C++ functions.
217 \li QML signals are transformed into C++ Qt signals.
218 \li QML enumerations are converted into C++ enumerations with \c{Q_ENUM}
219 declarations.
220\endlist
221
222An additional detail is the way \c qmltc generates class names. A class name for
223a given QML type is automatically deduced from the QML document defining that
224type: the QML file name without extensions (up to and excluding the first \c{.},
225also known as the base name) becomes a class name. The file name case is
226preserved. Thus, \c{HelloWorld.qml} would result in a \c{class HelloWorld} and
227\c{helloWoRlD.qml} in a \c{class helloWoRlD}. Following the QML convention, if a
228QML document file name starts with a lower-case letter, the generated C++ class
229is assumed to be anonymous and marked with \l{QML_ANONYMOUS}.
230
231For now, although the generated code is ready to be used from the C++
232application side, you should generally limit calls to the generated APIs.
233Instead, prefer implementing the application logic in QML/JavaScript and
234hand-written C++ types exposed to QML, using the qmltc-created classes for
235simple object instantiation. While generated C++ gives you direct (and usually
236faster) access to QML-defined elements of the type, understanding such code
237could be a challenge.
238
239\section2 Known Limitations
240
241Despite covering many common QML features, qmltc is still in the early stage of
242development with some things yet to be supported.
243
244Imported QML modules that consist of QML-defined types (such as
245\c{QtQuick.Controls}) might not get compiled correctly, even if those QML-defined
246types were compiled by qmltc..
247At present, you can reliably use \c{QtQml} and \c{QtQuick} modules as well as any
248other QML module that \b{only} contains C++ classes exposed to QML.
249
250On top of this, there are some more fundamental peculiarities to consider:
251
252\list
253 \li Qt's QML modules usually rely on C++ libraries to do the heavy lifting.
254 Often enough, these libraries do not provide public C++ API (since their
255 primary usage is through QML). For the users of qmltc, this means that their
256 apps need to link against private Qt libraries.
257
258 \li Due to the nature of qmltc code generation, QML plugins are unusable for
259 compilation purposes. Instead, QML modules - that use a plugin - have to
260 ensure that the plugin data is accessible at compile time. Such QML modules
261 would then have \e optional plugins. In most cases, the compile-time
262 information can be provided through a header file (with C++ declarations)
263 and linkable library (with C++ definitions). The user code is responsible
264 (usually through CMake) for including a path to the header file and linking
265 against the QML module library.
266\endlist
267
268\note
269Given the tech preview status of the compiler, you might also encounter bugs in
270qmltc, in the generated code, or some other related part. We encourage you to
271\l{https://bugreports.qt.io/}{submit a bug report} in this case.
272
273*/