aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrei Golubev <andrei.golubev@qt.io>2022-01-19 17:26:58 +0100
committerAndrei Golubev <andrei.golubev@qt.io>2022-01-27 11:29:55 +0100
commit2398f4bdff283789b001bee9f0997db521c86677 (patch)
tree1f5180c37da352ee35e7deb97dd9c0e6f99f8349
parentf90aba4a53d7b30a963154e01010e683c9e6a869 (diff)
Document qmltc output format
Make understanding qmltc output easier by showing how it works on a trivial example. Add a paragraph on the generated code location as an addition Fixes: QTBUG-100051 Change-Id: I0fa0f2c6c60fef7accbe855159275591d9e8bbc6 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> (cherry picked from commit 8ec6b990e13c8b41d0415af83c5890ef7a9ee227)
-rw-r--r--src/qml/doc/snippets/qmltc/CMakeLists.txt33
-rw-r--r--src/qml/doc/snippets/qmltc/special/HelloWorld.qml66
-rw-r--r--src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp85
-rw-r--r--src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp70
-rw-r--r--src/qml/doc/src/cmake/qt_target_compile_qml_to_cpp.qdoc17
-rw-r--r--src/qml/doc/src/qtqml-tool-qmltc.qdoc75
6 files changed, 323 insertions, 23 deletions
diff --git a/src/qml/doc/snippets/qmltc/CMakeLists.txt b/src/qml/doc/snippets/qmltc/CMakeLists.txt
index c3d8e4ecf3..69bfda7249 100644
--- a/src/qml/doc/snippets/qmltc/CMakeLists.txt
+++ b/src/qml/doc/snippets/qmltc/CMakeLists.txt
@@ -3,6 +3,8 @@ set(application_name tst_qmltc_examples)
#! [qmltc-app-name]
# Use "my_qmltc_example" as an application name:
set(application_name my_qmltc_example)
+
+# Create a CMake target, add C++ source files, link libraries, etc...
#! [qmltc-app-name]
]]
@@ -18,17 +20,31 @@ qt_internal_add_test(${application_name}
Qt::Gui
)
-#! [qmltc-add-qml-module]
-# Create a CMake target, add C++ source files, link libraries, etc...
+#! [qmltc-qml-files]
+# Specify a list of QML files to be compiled:
+set(application_qml_files
+ myApp.qml
+ MyButton.qml
+ MySlider.qml
+)
+#! [qmltc-qml-files]
+# files "invisible" to the documentation:
+list(APPEND application_qml_files
+ special/HelloWorld.qml
+)
+target_compile_definitions(${application_name} PRIVATE
+ QT_USE_QSTRINGBUILDER
+ QMLTC_TESTS_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
+ QMLTC_TESTS_BINARY_DIR="${CMAKE_CURRENT_BINARY_DIR}"
+)
+
+#! [qmltc-add-qml-module]
# Make the application into a proper QML module:
qt6_add_qml_module(${application_name}
VERSION 1.0
URI QmltcExample
- QML_FILES
- myApp.qml
- MyButton.qml
- MySlider.qml
+ QML_FILES ${application_qml_files}
)
#! [qmltc-add-qml-module]
@@ -39,9 +55,6 @@ target_link_libraries(${application_name} PRIVATE Qt::QmlPrivate Qt::QuickPrivat
# Compile qml files (listed in FILES) to C++ using qmltc and add these files to
# the application binary:
qt6_target_compile_qml_to_cpp(${application_name}
- QML_FILES
- myApp.qml
- MyButton.qml
- MySlider.qml
+ QML_FILES ${application_qml_files}
)
#! [qmltc-compile-to-cpp]
diff --git a/src/qml/doc/snippets/qmltc/special/HelloWorld.qml b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml
new file mode 100644
index 0000000000..9b85f02dde
--- /dev/null
+++ b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [qmltc-hello-world-qml]
+// HelloWorld.qml
+import QtQml
+
+QtObject {
+ id: me
+ property string hello: "Hello, qmltc!"
+
+ function printHello(prefix: string, suffix: string) {
+ console.log(prefix + me.hello + suffix);
+ }
+
+ signal created()
+ Component.onCompleted: me.created();
+}
+//! [qmltc-hello-world-qml]
diff --git a/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp
new file mode 100644
index 0000000000..ca1d438d2c
--- /dev/null
+++ b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/* Disclaimer: This file is an "as is" copy of the C++ header generated for the
+ accompanying HelloWorld.qml. Its pieces are used to:
+
+ * verify that the generated code is similar to what is contained in this file
+ * provide documentation snippets for the qmltc docs
+
+ Note: all the stuff below MAGIC_QMLTC_TEST_DELIMITER_LINE comment
+ participates in the aforementioned activities. Prefer to put arbitrary stuff
+ before that comment.
+*/
+
+// MAGIC_QMLTC_TEST_DELIMITER_LINE
+
+//! [qmltc-hello-world-generated]
+class HelloWorld : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(QString hello WRITE setHello READ hello BINDABLE bindableHello)
+public:
+ HelloWorld(QQmlEngine * engine, QObject * parent = nullptr);
+
+public:
+ void setHello(const QString& hello_);
+ QString hello();
+ QBindable<QString> bindableHello();
+
+ Q_INVOKABLE void printHello(QString prefix, QString suffix);
+
+signals:
+ void created();
+
+ // ...
+};
+//! [qmltc-hello-world-generated]
diff --git a/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp b/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp
index 8de430de86..50436dc26f 100644
--- a/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp
+++ b/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp
@@ -52,10 +52,12 @@
// test sources license. This is intentional to comply with default
// snippet license.
-#include <QtGui/qguiapplication.h>
-#include <QtCore/qtimer.h>
#include <QtTest/qtest.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qtimer.h>
+#include <QtGui/qguiapplication.h>
#include <QtQuick/qquickwindow.h>
+
//! [qqmlcomponent-include]
#include <QtQml/qqmlcomponent.h>
//! [qqmlcomponent-include]
@@ -64,6 +66,8 @@
#include "myapp.h" // include generated C++ header
//! [qmltc-include]
+#include <algorithm>
+
class tst_qmltc_examples : public QObject
{
Q_OBJECT
@@ -77,6 +81,8 @@ public:
private slots:
void app();
void appComponent();
+
+ void helloWorld();
};
#define CREATE_DUMMY_ARGC_ARGV() \
@@ -147,6 +153,66 @@ void tst_qmltc_examples::appComponent()
app.exec();
}
+#if !defined(QMLTC_TESTS_SOURCE_DIR) || !defined(QMLTC_TESTS_BINARY_DIR)
+# error "Tests assume that QMLTC_TESTS_{SOURCE,BINARY}_DIR are specified (through CMake)"
+#endif
+
+// Note: QtTest macros need to be in void-returning function, so use output arg.
+template<typename Predicate>
+void readFileContent(QStringList *content, const QString &url, Predicate filter)
+{
+ QVERIFY(content);
+
+ QFile file(url);
+ QVERIFY(file.exists());
+ QVERIFY(file.open(QIODeviceBase::ReadOnly | QIODeviceBase::Text));
+
+ QTextStream stream(&file);
+ while (!stream.atEnd()) {
+ QString line = stream.readLine();
+ if (filter(line))
+ content->append(std::move(line));
+ }
+}
+
+void tst_qmltc_examples::helloWorld()
+{
+ QStringList generatedCode;
+ readFileContent(&generatedCode,
+ QStringLiteral(QMLTC_TESTS_BINARY_DIR)
+ + u"/.qmltc/tst_qmltc_examples/helloworld.h",
+ [](const QString &) { return true; });
+ if (QTest::currentTestFailed())
+ QFAIL("Reading _generated_ C++ content for special/HelloWorld.qml failed");
+
+ QStringList documentationCode;
+ const auto filterDocumentationLines = [encounteredStart = false](QStringView line) mutable {
+ if (line.startsWith(u"// MAGIC_QMLTC_TEST_DELIMITER_LINE")) {
+ encounteredStart = true;
+ return false; // we don't need this specific line
+ }
+ if (!encounteredStart)
+ return false;
+ line = line.trimmed();
+ return !line.isEmpty() && !line.startsWith(u"//");
+ };
+ readFileContent(&documentationCode,
+ QStringLiteral(QMLTC_TESTS_SOURCE_DIR) + u"/special/HelloWorld.qml.cpp",
+ filterDocumentationLines);
+ if (QTest::currentTestFailed())
+ QFAIL("Reading special/HelloWorld.qml.cpp failed");
+
+ QVERIFY(!generatedCode.isEmpty());
+ QVERIFY(!documentationCode.isEmpty());
+
+ auto begin = generatedCode.cbegin();
+ for (const QString &existingString : qAsConst(documentationCode)) {
+ auto pos = std::find(begin, generatedCode.cend(), existingString);
+ QVERIFY2(pos != generatedCode.cend(), qPrintable(u"Could not find: " + existingString));
+ begin = std::next(pos);
+ }
+}
+
#undef CREATE_DUMMY_ARGC_ARGV
QTEST_APPLESS_MAIN(tst_qmltc_examples)
diff --git a/src/qml/doc/src/cmake/qt_target_compile_qml_to_cpp.qdoc b/src/qml/doc/src/cmake/qt_target_compile_qml_to_cpp.qdoc
index 1382597065..2d1da9efe8 100644
--- a/src/qml/doc/src/cmake/qt_target_compile_qml_to_cpp.qdoc
+++ b/src/qml/doc/src/cmake/qt_target_compile_qml_to_cpp.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -32,7 +32,7 @@
\title qt_target_compile_qml_to_cpp
\target qt6_target_compile_qml_to_cpp
-\brief Compiles QML files (.qml) to C++ source code with Qml Type Compiler (qmltc).
+\brief Compiles QML files (.qml) to C++ source code with \l{Qml Type Compiler}{qmltc}.
\section1 Synopsis
@@ -50,11 +50,14 @@ qt_target_compile_qml_to_cpp(
\section1 Description
-This command compiles the object structure of the passed QML files to C++ using
-qmltc. By default, qmltc generates lower-case \c{.h} and \c{.cpp} files for a
-given \c{.qml} file, so an arbitrary \c{HelloWorld.qml} ends up being compiled
-into \c{helloworld.h} and \c{helloworld.cpp}. The generated files are then
-automatically added to the target.
+By default, \l{Qml Type Compiler}{qmltc} creates lower-case \c{.h} and \c{.cpp}
+files for a given \c{.qml} file. For example, \c{Foo.qml} ends up being compiled
+into \c{foo.h} and \c{foo.cpp}.
+
+The created C++ files are placed into a dedicated \c{.qmltc/<target>/}
+sub-directory of the \c BINARY_DIR of the \c target passed as an argument to
+this command. These files are then automatically added to the target sources and
+compiled as Qt C++ code along with other source files.
\note This command is in technology preview and may change in future releases.
diff --git a/src/qml/doc/src/qtqml-tool-qmltc.qdoc b/src/qml/doc/src/qtqml-tool-qmltc.qdoc
index 9b9515e6ec..1bdeae098c 100644
--- a/src/qml/doc/src/qtqml-tool-qmltc.qdoc
+++ b/src/qml/doc/src/qtqml-tool-qmltc.qdoc
@@ -38,9 +38,12 @@ QQmlComponent-based object creation. The qmltc is part of the 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. The
+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. In order to enable qmltc:
+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.
@@ -62,7 +65,7 @@ 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
+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
@@ -114,6 +117,8 @@ 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
@@ -180,7 +185,69 @@ and use \l{Defining QML Types from C++}{declarative QML type registration}.
the generated classes cannot be further exposed to QML and used through
QQmlComponent.
-\section2 Known limitations
+\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{qt_target_compile_qml_to_cpp}{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
+
+\note \c qmltc does not guarantee that the generated C++ stays API-, source- or
+binary-compatible between releases.
+
+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 many things yet to be supported. To name a few: