aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-type-compiler.qdoc
blob: 38f8fe039b2da246e66761047f98b9f378db22fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
\page qtqml-qml-type-compiler.html
\title QML type compiler
\brief A tool to compile QML types to C++ ahead of time.
\keyword qmltc
\ingroup qtqml-tooling

The QML type compiler, \c qmltc, is a tool shipped with Qt to translate QML
types into C++ types that are \e{ahead-of-time} compiled as part of the user
code. Using qmltc can lead to better run-time performance due to more
optimization opportunities available to the compiler compared to a
QQmlComponent-based object creation. The qmltc is part of the \l{Qt Quick Compiler}
toolchain.

By design, qmltc outputs user-facing code. That code is supposed to be utilized
by the C++ application directly, otherwise you won't see any benefit. This
generated code essentially replaces QQmlComponent and its APIs to create objects
from QML documents. You can find more information under \l{Using qmltc in a QML
application} and \l{Generated Output Basics}.

In order to enable qmltc:

\list
    \li Create a \l{qt_add_qml_module}{proper QML module} for your application.

    \li Invoke qmltc, for example, through the \l{qmltc-cmake}{CMake API}.

    \li \c{#include} the generated header file(s) in the application source
    code.

    \li Instantiate an object of the generated type.
\endlist

In this workflow qmltc usually runs during the build process. Thus, when qmltc
rejects a QML document (whether due to errors or warnings, or because of
constructs qmltc doesn't yet support), the build process will fail. This is
similar to how you receive qmllint errors when you enable the automatic
generation of linting targets during \l{qt_add_qml_module}{QML module creation}
and then attempt to "build" them to run the qmllint.

\warning qmltc is currently in a Tech Preview stage and might not compile an
arbitrary QML program (see \l{Known Limitations} for more details). When qmltc
fails, nothing is generated as your application cannot sensibly use the qmltc
output. If your program contains errors (or unsolvable warnings), they should be
fixed to enable the compilation. The general rule is to adhere to the best
practices and follow \l{qmllint Reference}{qmllint} advice.

\note \c qmltc does not guarantee that the generated C++ stays API-, source- or
binary-compatible between past or future versions, even patch versions.
Furthermore, qmltc-compiled apps using Qt's QML modules will require linking
against private Qt API. This is because Qt's QML modules do not usually provide
a public C++ API since their primary usage is through QML.


\section2 Using qmltc in a QML application

From the build system perspective, adding qmltc compilation is not much
different from adding qml cache generation. Naively, the build process could be
described as:

\image qmltc-compilation-scheme.png

While the real compilation process is much trickier, this diagram captures the
core components that qmltc uses: the QML files themselves and qmldir with
qmltypes information. Simpler applications typically have rather primitive
qmldir yet, in general, qmldir could be complex, providing essential, nicely
packed type information that qmltc relies on to perform correct QML-to-C++
translation.

Nevertheless, adding an extra build step is not enough in qmltc case. The
application code must also be modified to use qmltc-generated classes instead of
QQmlComponent or its higher-level alternatives.

\section3 Compiling QML code with qmltc

Qt, starting from Qt 6, uses CMake to build its various components. User
projects can - and are encouraged to - also use CMake to build their components
using Qt. Adding out-of-the-box qmltc compilation support to your project would
require a CMake-driven build flow as well since this flow is centered around
proper QML modules and their infrastructure.

The easy way to add qmltc compilation is by using the dedicated
\l{qmltc-cmake}{CMake API} as part of a QML module creation for the application.
Consider a simple application directory structure:

\badcode
.
├── CMakeLists.txt
├── myspecialtype.h     // C++ type exposed to QML
├── myspecialtype.cpp
├── myApp.qml           // main QML page
├── MyButton.qml        // custom UI button
├── MySlider.qml        // custom UI slider
└── main.cpp            // main C++ application file
\endcode

Then the CMake code would usually look similar to the following:

\snippet qmltc/CMakeLists.txt qmltc-app-name
\codeline
\snippet qmltc/CMakeLists.txt qmltc-qml-files
\codeline
\snippet qmltc/CMakeLists.txt qmltc-add-qml-module
\codeline
\snippet qmltc/CMakeLists.txt qmltc-compile-to-cpp

\section3 Using the Generated C++

Unlike in the case of QQmlComponent instantiation, the output of qmltc, being
C++ code, is used directly by the application. Generally, constructing a new
object in C++ is equivalent to creating a new object through
QQmlComponent::create(). Once created, the object could be manipulated from C++
or, for example, combined with QQuickWindow to be drawn on screen.

If a compiled type exposes some required properties, `qmltc` will
require an initial value for those properties in the constructor for
the generated object.

Additionally, the constructor for a qmltc object can be provided with
with a callback to set up initial values for the component's
properties.

Given a \c{myApp.qml} file, the application code (in both cases) would
typically look like this:

\if defined(onlinedocs)
    \tab {generated-c++}{tab-qqmlcomponent}{Using QQmlComponent}{checked}
    \tab {generated-c++}{tab-qmltc}{Using qmltc-generated class}{}
    \tabcontent {tab-qqmlcomponent}
\else
   \section4 Using QQmlComponent
\endif
\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-include
\codeline
\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-0
\codeline
\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-1
\codeline
\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-2
\codeline
\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-exec
\if defined(onlinedocs)
    \endtabcontent
    \tabcontent {tab-qmltc}
\else
    \section4 Using qmltc-generated class
\endif
\snippet qmltc/tst_qmltc_examples.cpp qmltc-include
\codeline
\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-code
\codeline
\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-exec
\if defined(onlinedocs)
    \endtabcontent
\endif

\section2 QML engine

The generated code uses QQmlEngine to interact with dynamic parts of a QML
document - mainly the JavaScript code. For this to work, no special arrangements
are needed. Any QQmlEngine instance passed to the constructor of a
qmltc-generated class object should work correctly as does
\c{QQmlComponent(engine)}. This also means that you can use
\l{QQmlEngine}{QQmlEngine methods} that affect QML behavior. However, there are
caveats. Unlike QQmlComponent-based object creation, qmltc itself \e{does not}
rely on QQmlEngine when compiling the code to C++. For instance,
\c{QQmlEngine::addImportPath("/foo/bar/")} - normally resulting in an additional
import path to scan for - would be completely ignored by the ahead-of-time qmltc
procedure.

\note To add import paths to the qmltc compilation, consider using a relevant
argument of the \l{qmltc-cmake}{CMake command} instead.

Generally, you can think of it this way: QQmlEngine involves the application
process to run, while qmltc does not as it operates \e{before} your application
is even compiled. Since qmltc makes no attempt to introspect your application's
C++ source code, there is no way for it to know about certain kinds of QML
manipulations you, as a user, do. Instead of using QQmlEngine and related
run-time routines to expose types to QML, adding import paths, etc. you are,
practically, required to create \l{qt_add_qml_module}{well-behaving QML modules}
and use \l{Defining QML Types from C++}{declarative QML type registration}.

\warning Despite qmltc working closely with QQmlEngine and creating C++ code,
the generated classes cannot be further exposed to QML and used through
QQmlComponent.

\section2 Generated Output Basics

\c qmltc aims to be compatible with the existing QML execution model. This
implies that the generated code is roughly equivalent to the internal
QQmlComponent setup logic and thus you should be able to understand your QML
type's behavior, semantics and API the same way you do currently - by visually
inspecting the corresponding QML document.

However, the generated code is still somewhat confusing, especially given that
your application should use the qmltc output on the C++ side directly. There are
two parts of the generated code: CMake build files structure and the generated
C++ format. The former is covered in the \l{qmltc-cmake}{CMake API of qmltc} and
the latter is covered here.

Consider a simple HelloWorld type, that has a \c hello property, a function to
print that property, and a signal emitted when the object of that type is
created:

\snippet qmltc/special/HelloWorld.qml qmltc-hello-world-qml

When providing a C++ alternative of this QML type, the C++ class would need a
\l{Overview - QML and C++ Integration}{QML-specific meta-object system macro},
Q_PROPERTY decoration for the \c hello property, \c{Q_INVOKABLE} C++ printing
function and a regular Qt signal definition. Similarly, qmltc would translate
the given HelloWorld type into roughly the following:

\snippet qmltc/special/HelloWorld.qml.cpp qmltc-hello-world-generated

Even though specific details of the generated type could differ, the universal
aspects remain. For instance:

\list
    \li QML types within a document are translated into C++ types, according to
        the compiler-visible information.
    \li Properties are translated into C++ properties with Q_PROPERTY
        declarations.
    \li JavaScript functions become \c{Q_INVOKABLE} C++ functions.
    \li QML signals are transformed into C++ Qt signals.
    \li QML enumerations are converted into C++ enumerations with \c{Q_ENUM}
        declarations.
\endlist

An additional detail is the way \c qmltc generates class names. A class name for
a given QML type is automatically deduced from the QML document defining that
type: the QML file name without extensions (up to and excluding the first \c{.},
also known as the base name) becomes a class name. The file name case is
preserved. Thus, \c{HelloWorld.qml} would result in a \c{class HelloWorld} and
\c{helloWoRlD.qml} in a \c{class helloWoRlD}. Following the QML convention, if a
QML document file name starts with a lower-case letter, the generated C++ class
is assumed to be anonymous and marked with \l{QML_ANONYMOUS}.

For now, although the generated code is ready to be used from the C++
application side, you should generally limit calls to the generated APIs.
Instead, prefer implementing the application logic in QML/JavaScript and
hand-written C++ types exposed to QML, using the qmltc-created classes for
simple object instantiation. While generated C++ gives you direct (and usually
faster) access to QML-defined elements of the type, understanding such code
could be a challenge.

\section2 Known Limitations

Despite covering many common QML features, qmltc is still in the early stage of
development with some things yet to be supported.

Imported QML modules that consist of QML-defined types (such as
\c{QtQuick.Controls}) might not get compiled correctly, even if those QML-defined
types were compiled by qmltc..
At present, you can reliably use \c{QtQml} and \c{QtQuick} modules as well as any
other QML module that \b{only} contains C++ classes exposed to QML.

On top of this, there are some more fundamental peculiarities to consider:

\list
    \li Qt's QML modules usually rely on C++ libraries to do the heavy lifting.
    Often enough, these libraries do not provide public C++ API (since their
    primary usage is through QML). For the users of qmltc, this means that their
    apps need to link against private Qt libraries.

    \li Due to the nature of qmltc code generation, QML plugins are unusable for
    compilation purposes. Instead, QML modules - that use a plugin - have to
    ensure that the plugin data is accessible at compile time. Such QML modules
    would then have \e optional plugins. In most cases, the compile-time
    information can be provided through a header file (with C++ declarations)
    and linkable library (with C++ definitions). The user code is responsible
    (usually through CMake) for including a path to the header file and linking
    against the QML module library.
\endlist

\note
Given the tech preview status of the compiler, you might also encounter bugs in
qmltc, in the generated code, or some other related part. We encourage you to
\l{https://bugreports.qt.io/}{submit a bug report} in this case.

*/