diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2020-08-10 11:50:39 +0200 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2020-08-13 14:02:32 +0200 |
commit | 7e94cf8296859ea72a7caaf1a454fe6767fce450 (patch) | |
tree | 657569f12a48a3a1c414b9dda2f307a8b2821fcd /src | |
parent | 9a79791e7975de811102f89686a5d631eeac9d16 (diff) |
CMake: Initial work on handling Qt Qml static plugins
In Qt 5 we added a QmlImportScanner package that provided a
qt5_import_qml_plugins() function. Calling it with a target ensured
running qmlimportscanner to find and link necessary qml static plugins
for an application to work.
This is the initial port of that to Qt 6, with a few differences.
It introduces 2 function names, the version-less one and qt6_ prefixed
one, qt_import_qml_plugins and qt6_import_qml_plugins.
Implementation notes.
In Qt 5 we figured out what link flags to pass by parsing the plugin
prl files.
In Qt 6, CMake can generate appropriate plugin targets with
dependencies, as well as Config files that can look for the dependent
packages. Use that information for finding the dependencies and linking.
Note this relies on the assumption that find_package(Qt6Qml) will already
include all available Qml plugin Config files, so that the
targets exist in scope by the time qt_import_qml_plugins is called and
links against those targets.
The automatic inclusion is handled by a change in qtbase.
In Qt 5 the function was available as part of QmlImportScanner
package. In Qt 6 the function is moved to QmlMacros, so it's enough to
find_package(Qt6Qml) to use it.
A dummy QmlImportScanner package is provided for backwards
compatibility, that simply finds the Qml package.
Another change is to make the resource name added by
qt6_target_qml_files unique (include the target name), because
qmlcachegen generates a cpp file with a static initializer function
name that includes the resource name. This caused duplicate symbol
errors when linking more than one qtquickcontrols2 style plugin into
an application when the resource name was not unique.
There are a couple of TODOs left:
- Figure out if it's possible to automatically call
qt_import_qml_plugins for applications that link against Qml.
Perhaps using our hacky scope finalizers.
- Figure out how to handle scanning of resources.
Task-number: QTBUG-85961
Task-number: QTBUG-85994
Change-Id: I42f61e4acc6f74a3cdc030dba9e41ce789bc28f6
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/qml/CMakeLists.txt | 11 | ||||
-rw-r--r-- | src/qml/Qt6QmlImportScannerTemplate.cpp.in | 5 | ||||
-rw-r--r-- | src/qml/Qt6QmlMacros.cmake | 132 |
3 files changed, 147 insertions, 1 deletions
diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt index 461b2b8d7c..65f59e0247 100644 --- a/src/qml/CMakeLists.txt +++ b/src/qml/CMakeLists.txt @@ -658,3 +658,14 @@ set_target_properties(Qml PROPERTIES qt6_qml_type_registration(Qml) include(Qt6QmlBuildInternals.cmake) # special case + +# special case begin +# Install Qml import scanner template cpp file. +set(target "Qml") +set(path_suffix "${INSTALL_CMAKE_NAMESPACE}${target}") +qt_path_join(config_install_dir ${QT_CONFIG_INSTALL_DIR} ${path_suffix}) +qt_copy_or_install(FILES + "${CMAKE_CURRENT_LIST_DIR}/${INSTALL_CMAKE_NAMESPACE}${target}ImportScannerTemplate.cpp.in" + DESTINATION "${config_install_dir}" +) +# special case end diff --git a/src/qml/Qt6QmlImportScannerTemplate.cpp.in b/src/qml/Qt6QmlImportScannerTemplate.cpp.in new file mode 100644 index 0000000000..715dd87cb2 --- /dev/null +++ b/src/qml/Qt6QmlImportScannerTemplate.cpp.in @@ -0,0 +1,5 @@ +// This file is autogenerated by CMake. It imports static plugin classes for +// static plugins used by QML imports. +#include <QtPlugin> + +@qt_qml_import_cpp_file_content@ diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake index 24a5eb533b..bf59fba446 100644 --- a/src/qml/Qt6QmlMacros.cmake +++ b/src/qml/Qt6QmlMacros.cmake @@ -429,7 +429,8 @@ function(qt6_target_qml_files target) math(EXPR new_count "${resource_count} + 1") set_target_properties(${target} PROPERTIES QT6_QML_MODULE_ADD_QML_FILES_COUNT ${new_count}) - qt6_add_resources(${target} "qml_files${new_count}" + # TODO: Fix this if it blows up on Windows due to too long target name. + qt6_add_resources(${target} "${target}_qml_files_resource_${new_count}" FILES ${arg_FILES} OUTPUT_TARGETS resource_targets ) @@ -833,3 +834,132 @@ function(qt6_quick_compiler_process_resources target resource_name) set(${arg_OUTPUT_REMAINING_RESOURCES} ${resource_files} PARENT_SCOPE) set(${arg_OUTPUT_RESOURCE_NAME} ${resource_name} PARENT_SCOPE) endfunction() + +include(CMakeParseArguments) + +function(qt6_import_qml_plugins target) + if(${QT_CMAKE_EXPORT_NAMESPACE}_IS_SHARED_LIBS_BUILD) + return() + endif() + set(options) + set(oneValueArgs "PATH_TO_SCAN") + set(multiValueArgs) + + cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if(NOT arg_PATH_TO_SCAN) + set(arg_PATH_TO_SCAN "${CMAKE_CURRENT_SOURCE_DIR}") + endif() + + # Find location of qmlimportscanner. + get_target_property(tool_path ${QT_CMAKE_EXPORT_NAMESPACE}::qmlimportscanner IMPORTED_LOCATION) + if(NOT tool_path) + set(configs "RELWITHDEBINFO;RELEASE;MINSIZEREL;DEBUG") + foreach(config ${configs}) + get_target_property(tool_path Qt6::qmlimportscanner IMPORTED_LOCATION_${config}) + if(tool_path) + break() + endif() + endforeach() + endif() + + if(NOT EXISTS "${tool_path}") + message(FATAL_ERROR "The package \"QmlImportScanner\" references the file + \"${tool_path}\" +but this file does not exist. Possible reasons include: +* The file was deleted, renamed, or moved to another location. +* An install or uninstall procedure did not complete successfully. +* The installation package was faulty. +") + endif() + + # Find location of qml dir. + # TODO: qt.prf implies that there might be more than one qml import path to pass to + # qmlimportscanner. + set(qml_path "${QT6_INSTALL_PREFIX}/${QT6_INSTALL_QML}") + + # Small macro to avoid duplicating code in two different loops. + macro(_qt6_QmlImportScanner_parse_entry) + set(entry_name "qml_import_scanner_import_${idx}") + cmake_parse_arguments("entry" + "" + "CLASSNAME;NAME;PATH;PLUGIN;RELATIVEPATH;TYPE;VERSION;" "" + ${${entry_name}}) + endmacro() + + # Run qmlimportscanner and include the generated cmake file. + set(qml_imports_file_path + "${CMAKE_CURRENT_BINARY_DIR}/Qt6_QmlPlugins_Imports_${target}.cmake") + + # TODO: QTBUG-85994 Figure out how to handle resources like in fix for QTBUG-82873. + message(STATUS "Running qmlimportscanner to find used QML plugins. ") + execute_process(COMMAND + "${tool_path}" "${arg_PATH_TO_SCAN}" -importPath "${qml_path}" + -cmake-output + OUTPUT_FILE "${qml_imports_file_path}") + + include("${qml_imports_file_path}" OPTIONAL RESULT_VARIABLE qml_imports_file_path_found) + if(NOT qml_imports_file_path_found) + message(FATAL_ERROR "Could not find ${qml_imports_file_path} which was supposed to be generated by qmlimportscanner.") + endif() + + # Parse the generated cmake file. + # It is possible for the scanner to find no usage of QML, in which case the import count is 0. + if(qml_import_scanner_imports_count) + set(added_plugins "") + foreach(idx RANGE "${qml_import_scanner_imports_count}") + _qt6_QmlImportScanner_parse_entry() + if(entry_PATH AND entry_PLUGIN) + # Sometimes a plugin appears multiple times with different versions. + # Make sure to process it only once. + list(FIND added_plugins "${entry_PLUGIN}" _index) + if(NOT _index EQUAL -1) + continue() + endif() + list(APPEND added_plugins "${entry_PLUGIN}") + + # Link against the Qml plugin. The assumption is that all Qml plugins are already + # find_package()'d by the Qml package, so we can safely link against the target. + target_link_libraries("${target}" PRIVATE + "${QT_CMAKE_EXPORT_NAMESPACE}::${entry_PLUGIN}") + endif() + endforeach() + + # Generate content for plugin initialization cpp file. + set(added_imports "") + set(qt_qml_import_cpp_file_content "") + foreach(idx RANGE "${qml_import_scanner_imports_count}") + _qt6_QmlImportScanner_parse_entry() + if(entry_PLUGIN) + if(entry_CLASSNAME) + list(FIND added_imports "${entry_PLUGIN}" _index) + if(_index EQUAL -1) + string(APPEND qt_qml_import_cpp_file_content + "Q_IMPORT_PLUGIN(${entry_CLASSNAME})\n") + list(APPEND added_imports "${entry_PLUGIN}") + endif() + else() + message(FATAL_ERROR + "Plugin ${entry_PLUGIN} is missing a classname entry, please add one to the qmldir file.") + endif() + endif() + endforeach() + + # Write to the generated file, and include it as a source for the given target. + set(generated_import_cpp_path + "${CMAKE_CURRENT_BINARY_DIR}/Qt6_QmlPlugins_Imports_${target}.cpp") + configure_file("${Qt6Qml_DIR}/Qt6QmlImportScannerTemplate.cpp.in" + "${generated_import_cpp_path}" + @ONLY) + target_sources(${target} PRIVATE "${generated_import_cpp_path}") + endif() +endfunction() + +if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) + function(qt_import_qml_plugins) + if(QT_DEFAULT_MAJOR_VERSION EQUAL 5) + qt5_import_qml_plugins(${ARGV}) + elseif(QT_DEFAULT_MAJOR_VERSION EQUAL 6) + qt6_import_qml_plugins(${ARGV}) + endif() + endfunction() +endif() |