// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
\page qt6-port-to-qt-add-qml-module.html
\title Port QML modules to CMake
\brief Port your QML modules to the qt_add_qml_module CMake API.

QML modules have become more powerful and easier to use in Qt 6. The following sections describe
how to port QML modules to the \l qt_add_qml_module CMake API.

See also \l{Modernizing QML Modules} on how to modernize a QML module that already uses \l
qt_add_qml_module.

\section1 Identify issues to fix

Use \l qmllint to support you through the process.

Each QML module defined with \l qt_add_qml_module has a \c{_qmllint} CMake target that you can
use to identify potential issues or improvements. For a QML module called \c MyQmlLibrary use
\c{MyQmlLibrary_qmllint}, for example. To run \l qmllint on all QML modules, use \c
{all_qmllint}.

The warning categories of \l qmllint that hint at QML module issues are:
\list
    \li \l{Warnings occurred while importing}{[import]}
    \li \l{Unused imports}{[unused-imports]}
    \li \l{Unresolved type}{[unresolved-type]}
    \li \l{Unresolved Alias}{[unresolved-alias]}
    \li \l{Missing enum entry}{[missing-enum-entry]}
    \li \l{Missing property}{[missing-property]}
    \li \l{Missing type}{[missing-type]}
\endlist

\section1 Prepare the project for qt_add_qml_module

\section2 Make qt_add_qml_module available in CMake

To make \l qt_add_qml_module available in CMake, add \c Core and \c Qml to your \c find_package
call in the project's top-level \c CMakeLists.txt file:
\badcode
find_package(Qt6 REQUIRED COMPONENTS Core Qml)
\endcode

\section2 Use qt_standard_project_setup

\l qt_standard_project_setup sets up \l{Qt CMake policies} needed for \l qt_add_qml_module, among
other things.

Call \l qt_standard_project_setup in the project's top-level \c CMakeLists.txt file
before any \l qt_add_qml_module call:
\badcode
qt_standard_project_setup(REQUIRES 6.8)
\endcode

\section1 Use qt_add_qml_module

\l qt_add_qml_module is the CMake function that takes care of generating QML modules. It
automatically generates \c qmldir and \c qmltypes files, and sets up tooling like \l qmlcachegen
or \l qmllint.

QML modules can be added to both executable and library targets in CMake. QML modules attached
to the executable target can't be used or linked by other executables, while QML modules attached
to library targets can.

\section2 Add a QML module to your executable target
In this case, the source files of the QML module are treated as part of the executable itself,
rather than being compiled into a separate library. This means neither a module nor plugin library
for this module is created—--the module is fully integrated into the executable. As a result, the
module is tied to that specific program and cannot be reused by other executables or libraries.

To add a QML module to your executable, in your \c CMakeLists.txt:
\badcode
# pre-existing:
qt_add_executable(MyApp main.cpp)

# add this
qt_add_qml_module(MyApp
    URI MyAppModule
    QML_FILES
        Main.qml # and possibly more .qml files
)
\endcode

The \c Main.qml should start with an upper case letter so that it can be instantiated by
\c loadFromModule methods like \l{QQmlApplicationEngine::loadFromModule} or
\l{QQmlComponent::loadFromModule}.
Also, the QML module URI should be different from the target name to avoid name clashes
in the build folder.

\section2 Add a QML module to your library target
To add a QML module to your library, in your \c CMakeLists.txt:
\badcode
qt_add_qml_module(MyQmlLibrary
    URI MyQmlModule
    QML_FILES MyQmlComponent1.qml MyQmlComponent2.qml...
    SOURCES MyCppComponent1.h MyCppComponent1.cpp MyCppComponent2.h MyCppComponent2.cpp...
    RESOURCES MyResource1.png MyResource2.png...
)
\endcode

\l qt_add_qml_module creates a \c SHARED library via \l qt_add_library if the \c
MyQmlLibrary target does not exist yet, like in this example.

\note Your QML module URI should be different from the target name to avoid name clashes in the
build folder.

\section1 Use loadFromModule to load your QML files

Use \c loadFromModule to load your QML file, for example:
\badcode
engine.load(QUrl(QStringLiteral("qrc:/MyQmlModule/Main.qml")));
// becomes
engine.loadFromModule("MyQmlModule", "Main");
\endcode

\section1 Remove handwritten qmldir files

\l qt_add_qml_module automatically generates \c qmldir files. If you have singletons in
your \c qmldir, declare them in your \c CMakeLists.txt before the \l qt_add_qml_module call with:
\badcode
set_source_files_properties(MySingleton.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
\endcode

Delete the handwritten \c qmldir after that.

\section1 Remove qmltypes files generated by qmlplugindump

\l qt_add_qml_module auto-generates \c qmltypes files when all your types are using
\l{Registering C++ Types with the QML Type System}{declarative type registration}, which removes
the need to generate \c qmltypes files by hand using tools like \c qmlplugindump.

To achieve that, remove manual calls to \c qmlRegisterType and its variants. Then,
\l{Registering C++ Types with the QML Type System}{register your types declaratively} by using
\l QML_ELEMENT, for example:
\badcode
// add this header
#include <QtQml/qqmlregistrations.h>

class MyComponent: public QObject {
    Q_OBJECT

    // add this line to register MyComponent as 'MyComponent' in QML.
    QML_ELEMENT
    ....
};
\endcode

See \l{Registering C++ Types with the QML Type System} on how to handle more complicated
registration cases like foreign type registration.

Delete the handwritten \c qmltypes files after that.

\section1 Remove handwritten type registration plugins

\l qt_add_qml_module can generate a \l {Creating C++ Plugins for QML}{QML module plugin}
automatically for you. You don't need a handwritten plugin if your plugin's only task is to do type
registration. Remove the plugin altogether if switching to declarative type registration did remove
all the code from your plugin.

Make sure that you \e remove the \c NO_PLUGIN, \c NO_PLUGIN_OPTIONAL, \c
NO_CREATE_PLUGIN_TARGET, and \c NO_GENERATE_PLUGIN_SOURCE arguments from \l qt_add_qml_module to
allow automatic plugin generation.

\section1 Remove qrc files

\l qt_add_qml_module automatically generates \c qrc files. To list resources in the \c qrc files,
like images or sound files, add them to \l{qt_add_qml_module}'s \c RESOURCES argument.
You can find the Module's resources under \c{:/qt/qml/MyQmlLibraryModule/}.

\section1 Replace directory imports with QML module imports

Replace directory imports with QML module imports. For example,
\badcode
import "content" // contains SomeType.qml
// becomes
import MyQmlModule // contains SomeType.qml

SomeType {
    ...
}
\endcode

Note that files inside a QML module automatically import their own QML module. You can remove the
self-imports.

\sa {Changes to Qt QML}, {Modernizing QML Modules}
*/
