diff options
Diffstat (limited to 'src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-type-compiler.qdoc')
-rw-r--r-- | src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-type-compiler.qdoc | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-type-compiler.qdoc b/src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-type-compiler.qdoc new file mode 100644 index 0000000000..38f8fe039b --- /dev/null +++ b/src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-type-compiler.qdoc @@ -0,0 +1,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. + +*/ |