aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2020-10-30 11:27:36 +0100
committerAlexandru Croitor <alexandru.croitor@qt.io>2020-11-12 18:06:36 +0100
commit8cdbcee614dbb34d4ac770bee1734c18ea27fa12 (patch)
treee992b2070de283c558df9e261580365d5ccf4937 /src/qml
parentccb9c17d29cb680dd01318f5e59e7edab26b73c6 (diff)
CMake: Allow building pure QML modules not backed by C++ sources
When no C++ sources are passed to qt_internal_add_qml_plugin (a pure QML module), create a C++ backed Qt plugin anyway. In such a case, generate a dummy plugin.cpp containing a QQmlEngineExtensionPlugin subclass. The class name is autogenerated from the QML import URI. The class constructor will call the qmltyperegistrar generated void qml_register_types_foo() function, to ensure the needed module versions are registered for the QML module. Change-Id: I19959dafdf0dc837c6037e7dc1d549b7420110a7 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/CMakeLists.txt6
-rw-r--r--src/qml/Qt6QmlBuildInternals.cmake34
-rw-r--r--src/qml/Qt6QmlMacros.cmake68
-rw-r--r--src/qml/Qt6QmlPluginTemplate.cpp.in23
4 files changed, 110 insertions, 21 deletions
diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt
index 68e5a94734..596a8fdfc0 100644
--- a/src/qml/CMakeLists.txt
+++ b/src/qml/CMakeLists.txt
@@ -668,4 +668,10 @@ qt_copy_or_install(FILES
"${CMAKE_CURRENT_LIST_DIR}/${INSTALL_CMAKE_NAMESPACE}${target}ImportScannerTemplate.cpp.in"
DESTINATION "${config_install_dir}"
)
+
+# Install pure QML Plugin template cpp file.
+qt_copy_or_install(FILES
+ "${CMAKE_CURRENT_LIST_DIR}/${INSTALL_CMAKE_NAMESPACE}${target}PluginTemplate.cpp.in"
+ DESTINATION "${config_install_dir}"
+)
# special case end
diff --git a/src/qml/Qt6QmlBuildInternals.cmake b/src/qml/Qt6QmlBuildInternals.cmake
index d25c987fc9..f6f1c98ecb 100644
--- a/src/qml/Qt6QmlBuildInternals.cmake
+++ b/src/qml/Qt6QmlBuildInternals.cmake
@@ -9,7 +9,6 @@ include_guard(GLOBAL)
# in an IDE. Finally, it will also create a custom ${target}_qmltypes which
# can be used to generate the respective plugins.qmltypes file.
#
-# CPP_PLUGIN: Whether this qml module has any c++ source files.
# URI: Module's uri.
# TARGET_PATH: Expected installation path for the Qml Module. Equivalent
# to the module's URI where '.' is replaced with '/'. Use this to override the
@@ -79,22 +78,15 @@ function(qt_internal_add_qml_module target)
${ARGV}
)
- # If we have no sources, but qml files, create a custom target so the
- # qml file will be visibile in an IDE.
- if (arg_SOURCES)
- qt_internal_add_plugin(${target}
- TYPE
- qml_plugin
- QML_TARGET_PATH
- "${arg_TARGET_PATH}"
- ${plugin_args}
- )
- endif()
-
+ qt_internal_add_plugin(${target}
+ TYPE
+ qml_plugin
+ QML_TARGET_PATH
+ "${arg_TARGET_PATH}"
+ ${plugin_args}
+ )
- if (arg_CPP_PLUGIN OR arg_SOURCES)
- set(no_create_option DO_NOT_CREATE_TARGET)
- endif()
+ set(no_create_option DO_NOT_CREATE_TARGET)
if (arg_CLASSNAME)
set(classname_arg CLASSNAME ${arg_CLASSNAME})
@@ -120,6 +112,15 @@ function(qt_internal_add_qml_module target)
set(install_qmltypes_arg INSTALL_QMLTYPES)
endif()
+
+ # Because qt_internal_add_qml_module does not propagate its SOURCES option to
+ # qt6_add_qml_module, but only to qt_internal_add_plugin, we need a way to tell
+ # qt6_add_qml_module if it should generate a dummy plugin cpp file. Otherwise we'd generate
+ # a dummy plugin.cpp file twice and thus cause duplicate symbol issues.
+ if (NOT arg_SOURCES)
+ set(pure_qml_module "PURE_MODULE")
+ endif()
+
qt_path_join(qml_module_install_dir ${QT_INSTALL_DIR} "${INSTALL_QMLDIR}/${arg_TARGET_PATH}")
set(qml_module_build_dir "")
@@ -140,6 +141,7 @@ function(qt_internal_add_qml_module target)
${classname_arg}
${generate_qmltypes_arg}
${install_qmltypes_arg}
+ ${pure_qml_module}
RESOURCE_PREFIX "/qt-project.org/imports"
TARGET_PATH ${arg_TARGET_PATH}
URI ${arg_URI}
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake
index 51fe4cac14..89afeb1d69 100644
--- a/src/qml/Qt6QmlMacros.cmake
+++ b/src/qml/Qt6QmlMacros.cmake
@@ -2,6 +2,8 @@
# Q6QmlMacros
#
+set(__qt_qml_macros_module_base_dir "${CMAKE_CURRENT_LIST_DIR}")
+
#
# Create a Qml Module. Arguments:
#
@@ -51,7 +53,7 @@
# information is required for all the QML modules that depend on a C++ plugin
# for additional functionality. Qt Quick applications built with static
# linking cannot resolve the module imports without this information.
-# (REQUIRED for static targets)
+# (REQUIRED for static QML modules backed by C++ sources aka non-pure QML modules)
#
# DESIGNER_SUPPORTED: Specify this argument if the plugin is supported by Qt
# Quick Designer. By default, the plugin will not be supported. (OPTIONAL)
@@ -84,10 +86,12 @@
# type registration functions are already available by other means, typically
# by linking a library proxied by the plugin, it won't be loaded.
#
+# PURE_MODULE: The plugin does not take any C++ source files. A dummy class plugin cpp file will
+# be generated to ensure the module is found by the Qml engine.
+#
# This function is currently in Technical Preview.
# It's signature and behavior might change.
function(qt6_add_qml_module target)
-
set(args_optional
GENERATE_QMLTYPES
INSTALL_QMLTYPES
@@ -96,6 +100,7 @@ function(qt6_add_qml_module target)
SKIP_TYPE_REGISTRATION
PLUGIN_OPTIONAL
INSTALL_QML_FILES
+ PURE_MODULE
)
if (QT_BUILDING_QT)
@@ -142,11 +147,18 @@ function(qt6_add_qml_module target)
message(FATAL_ERROR "qt6_add_qml_module called with an invalid version argument: '${arg_VERSION}'. Expected version style: VersionMajor.VersionMinor.")
endif()
- if (NOT BUILD_SHARED_LIBS AND NOT arg_CLASSNAME)
- message(FATAL_ERROR "qt6_add_qml_module Static builds of Qml modules require a class name, none was provided. Please specify one using the CLASSNAME argument.")
+ # If C++ sources were directly specified (not via qt_internal_add_qml_module), we assume the
+ # user will provide a plugin.cpp file. Don't generate a dummy plugin.cpp file in this case.
+ #
+ # If no sources were specified or the plugin was marked as a pure QML module, generate a
+ # dummy plugin.cpp file.
+ if (arg_SOURCES OR NOT arg_PURE_MODULE)
+ set(create_pure_qml_module_plugin FALSE)
+ else()
+ set(create_pure_qml_module_plugin TRUE)
endif()
- if (arg_DO_NOT_CREATE_TARGET AND NOT TARGET ${target})
+ if (arg_DO_NOT_CREATE_TARGET AND NOT TARGET "${target}")
message(FATAL_ERROR "qt6_add_qml_module called with DO_NOT_CREATE_TARGET, but the given target '${target}' is not a cmake target")
endif()
@@ -160,6 +172,9 @@ function(qt6_add_qml_module target)
message(FATAL_ERROR "qt6_add_qml_module called with DO_NOT_CREATE_TARGET, but target '${target}' is neither a static or a module library.")
endif()
else()
+ # TODO: Creating a library here means we're missing creation of supporting .prl files,
+ # as well as install(TARGET foo EXPORT bar) mapping, as opposed to when it's done
+ # by qt_internal_add_plugin inside the qt_internal_add_qml_module call.
if(NOT BUILD_SHARED_LIBS)
add_library(${target} STATIC)
set(is_static TRUE)
@@ -183,6 +198,10 @@ function(qt6_add_qml_module target)
string(REPLACE "." "/" arg_TARGET_PATH ${arg_URI})
endif()
+ if(create_pure_qml_module_plugin)
+ _qt_internal_create_dummy_qml_plugin("${target}" "${arg_URI}" arg_CLASSNAME)
+ endif()
+
if (ANDROID)
# Adjust Qml plugin names on Android similar to qml_plugin.prf which calls
# $$qt5LibraryTarget($$TARGET, "qml/$$TARGETPATH/").
@@ -459,6 +478,45 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
endfunction()
endif()
+# Creates a dummy Qml plugin class for pure Qml modules.
+# Needed for both shared and static Qt builds, so that the Qml engine knows to load the plugin.
+function(_qt_internal_create_dummy_qml_plugin target uri out_class_name)
+ # Use the escaped URI name as the basis for the class name.
+ string(REGEX REPLACE "[^A-Za-z0-9]" "_" escaped_uri "${uri}")
+
+ set(qt_qml_plugin_class_name "${escaped_uri}Plugin")
+ set(generated_cpp_file_name_base "Qt6_PureQmlModule_${target}_${qt_qml_plugin_class_name}")
+ set(qt_qml_plugin_moc_include_name "${generated_cpp_file_name_base}.moc")
+
+ set(register_types_function_name "qml_register_types_${escaped_uri}")
+ set(qt_qml_plugin_intro "extern void ${register_types_function_name}();")
+
+ if(QT_BUILDING_QT)
+ string(APPEND qt_qml_plugin_intro "\n\nQT_BEGIN_NAMESPACE")
+ set(qt_qml_plugin_outro "QT_END_NAMESPACE")
+ endif()
+
+ set(qt_qml_plugin_constructor_content
+ "volatile auto registration = &${register_types_function_name};
+ Q_UNUSED(registration);
+")
+
+ set(template_path "${__qt_qml_macros_module_base_dir}/Qt6QmlPluginTemplate.cpp.in")
+ set(generated_cpp_file_name "${generated_cpp_file_name_base}.cpp")
+ set(generated_cpp_file_path "${CMAKE_CURRENT_BINARY_DIR}/${generated_cpp_file_name}")
+
+ configure_file("${template_path}" "${generated_cpp_file_path}" @ONLY)
+
+ target_sources("${target}" PRIVATE "${generated_cpp_file_path}")
+ target_link_libraries("${target}" PRIVATE ${QT_CMAKE_EXPORT_NAMESPACE}::Qml)
+
+ set(${out_class_name} "${qt_qml_plugin_class_name}" PARENT_SCOPE)
+
+ # Enable AUTOMOC explicitly, because the generated cpp file expects to include its moc-ed
+ # output file.
+ set_property(TARGET "${target}" PROPERTY AUTOMOC ON)
+endfunction()
+
#
# Add Qml files (.qml,.js,.mjs) to a Qml module. This will also append the
# qml files to the qmldir file of the module. Two source file properties can
diff --git a/src/qml/Qt6QmlPluginTemplate.cpp.in b/src/qml/Qt6QmlPluginTemplate.cpp.in
new file mode 100644
index 0000000000..9788932743
--- /dev/null
+++ b/src/qml/Qt6QmlPluginTemplate.cpp.in
@@ -0,0 +1,23 @@
+// This file is autogenerated by CMake.
+// It facilitates usage of a pure QML module as a static QML plugin.
+
+#include <QtQml/qqmlextensionplugin.h>
+
+@qt_qml_plugin_intro@
+
+class @qt_qml_plugin_class_name@ : public QQmlEngineExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid)
+
+public:
+ @qt_qml_plugin_class_name@(QObject *parent = nullptr) : QQmlEngineExtensionPlugin(parent)
+ {
+ Q_UNUSED(parent);
+ @qt_qml_plugin_constructor_content@
+ }
+};
+
+@qt_qml_plugin_outro@
+
+#include "@qt_qml_plugin_moc_include_name@"