diff options
author | Leander Beernaert <leander.beernaert@qt.io> | 2019-06-05 13:39:41 +0200 |
---|---|---|
committer | Leander Beernaert <leander.beernaert@qt.io> | 2019-07-15 12:28:12 +0000 |
commit | 30b374637070581f04fc75287a32be943b00d422 (patch) | |
tree | b8f84973b3c7bc6ec488e9e27802890744d2ce10 | |
parent | faac0ef8c6f418aab019d7eb96d32178cddab4be (diff) |
Add support for QML plugins
Add the necessary code to both the QtBuild and pro2cmake to be able
to handle qml plugins in qt declarative.
Add condition replacement for QTDIR_build to QT_BUILDING_QT so that
certain qml examples work correctly when being built in the build
directory.
Fix add_qt_resources not being updated during build after changes
were made. Files list used as dependencies were not populated.
Add missing module mappings for qtdeclarative.
Change-Id: I0f71d0a3a0e7e97ba96807950d11cffaee04d9b2
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Reviewed-by: Qt CMake Build Bot
-rw-r--r-- | cmake/QtBuild.cmake | 145 | ||||
-rw-r--r-- | util/cmake/helper.py | 2 | ||||
-rwxr-xr-x | util/cmake/pro2cmake.py | 53 |
3 files changed, 195 insertions, 5 deletions
diff --git a/cmake/QtBuild.cmake b/cmake/QtBuild.cmake index fc2007517a..1ef14c152b 100644 --- a/cmake/QtBuild.cmake +++ b/cmake/QtBuild.cmake @@ -1455,10 +1455,19 @@ function(add_qt_plugin target) qt_internal_set_qt_known_plugins("${QT_KNOWN_PLUGINS}" "${target}") qt_parse_all_arguments(arg "add_qt_plugin" "STATIC;EXCEPTIONS" - "TYPE;CLASS_NAME;OUTPUT_DIRECTORY;INSTALL_DIRECTORY;ARCHIVE_INSTALL_DIRECTORY" + "TYPE;CLASS_NAME;OUTPUT_DIRECTORY;INSTALL_DIRECTORY;ARCHIVE_INSTALL_DIRECTORY;QML_TARGET_PATH" "${__default_private_args};${__default_public_args};DEFAULT_IF" ${ARGN}) set(output_directory_default "${QT_BUILD_DIR}/${INSTALL_PLUGINSDIR}/${arg_TYPE}") + set(install_directory_default "${INSTALL_PLUGINSDIR}/${arg_TYPE}") + set(archive_install_directory_default "${INSTALL_LIBDIR}/${arg_TYPE}") + + if (arg_QML_TARGET_PATH) + set(target_path "${arg_QML_TARGET_PATH}") + set(output_directory_default "${QT_BUILD_DIR}/${INSTALL_QMLDIR}/${target_path}") + set(install_directory_default "${INSTALL_QMLDIR}/${target_path}") + set(archive_install_directory_default "${INSTALL_QMLDIR}/${target_path}") + endif() if ("x${arg_CLASS_NAME}" STREQUAL x) message(AUTHOR_WARNING "add_qt_plugin called without setting CLASS_NAME.") @@ -1467,10 +1476,10 @@ function(add_qt_plugin target) qt_internal_check_directory_or_type(OUTPUT_DIRECTORY "${arg_OUTPUT_DIRECTORY}" "${arg_TYPE}" "${output_directory_default}" output_directory) qt_internal_check_directory_or_type(INSTALL_DIRECTORY "${arg_INSTALL_DIRECTORY}" "${arg_TYPE}" - "${INSTALL_PLUGINSDIR}/${arg_TYPE}" install_directory) + "${install_directory_default}" install_directory) qt_internal_check_directory_or_type(ARCHIVE_INSTALL_DIRECTORY "${arg_ARCHIVE_INSTALL_DIRECTORY}" "${arg_TYPE}" - "${INSTALL_LIBDIR}/${arg_TYPE}" archive_install_directory) + "${archive_install_directory_default}" archive_install_directory) if(arg_STATIC OR NOT BUILD_SHARED_LIBS) add_library("${target}" STATIC) @@ -1614,6 +1623,135 @@ function(add_qt_plugin target) qt_internal_add_linker_version_script(${target}) endfunction() +# Generate custom ${target}_qmltypes target for Qml Plugins +function(qt_add_qmltypes_target target) + + # Do nothing when cross compiling + if (CMAKE_CROSSCOMPILING) + return() + endif() + + qt_parse_all_arguments(arg "qt_generate_qmltypes" + "" + "TARGET_PATH;IMPORT_VERSION;IMPORT_NAME;CXX_MODULE;QML_PLUGINDUMP_DENDENCIES" + "" + ${ARGN}) + + # scan repos for qml repositories + foreach(repo IN LISTS QT_REPOS) + if (IS_DIRECTORY "${repo}/qml") + list(APPEND import_paths "${repo}/qml") + endif() + endforeach() + list(REMOVE_DUPLICATES import_paths) + if (UNIX) + list(JOIN import_paths ":" import_paths_env) + else() + list(JOIN import_paths "\;" import_paths_env) + endif() + + if(NOT arg_IMPORT_NAME) + string(REGEX REPLACE "\\.\\d+$" "" import_name ${arg_TARGET_PATH}) + else() + set(import_name ${arg_IMPORT_NAME}) + endif() + + if(NOT arg_IMPORT_VERSION) + if(NOT arg_CXX_MODULE) + if (NOT ${CMAKE_PROJECT_VERSION_MAJOR} OR NOT ${CMAKE_PROJECT_VERSION_MINOR}) + message(FATAL_ERROR "Please specify import version using IMPORT_VERSION") + endif() + set(import_version "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}") + else() + if(NOT TARGET arg_CXX_MODULE) + message(FATAL_ERROR "CXX_MODULE parameter must be a cmake target") + endif() + + get_target_property(qt_major_version ${arg_CXX_MODULE} INTERFACE_QT_MAJOR_VERSION) + if (NOT qt_major_version) + message(FATAL_ERROR "CXX_MODULE(${arg_CXX_MODULE} is not a valid Qt module") + endif() + + get_target_property(cxx_module_alias ${arg_CXX_MODULE} ALIASED_TARGET) + if (cxx_module_alias) + set(cxx_module_target ${cxx_module_alias}) + else() + set(cxx_module_target ${arg_CXX_MODULE}) + endif() + + set(import_version "${${cxx_module_target}_VERSION_MAJOR}.${${cxx_module_target}_VERSION_MINOR}") + endif() + else() + set(import_version ${arg_IMPORT_VERSION}) + endif() + + get_target_property(source_dir ${target} SOURCE_DIR) + + # qml1_target check is no longer required + set(qmltypes_command_args "-nonrelocatable") + if (NOT arg_QML_PLUGINDUMP_DENDENCIES AND EXISTS "${source_dir}/dependencies.json") + list(APPEND qmltypes_command_args "-dependencies" "${source_dir}/dependencies.json") + elseif(arg_QML_PLUGINDUMP_DENDENCIES) + list(APPEND qmltypes_command_args "-dependencies" "${arg_QML_PLUGINDUMP_DENDENCIES}") + endif() + + string(REPLACE "/" "." import_name_arg ${import_name}) + + list(APPEND qmltypes_command_args "${import_name_arg}" "${import_version}") + + add_custom_target( + "${target}_qmltypes" + DEPENDS qmlplugindump + COMMAND ${CMAKE_COMMAND} -E env "QML2_IMPORTPATH=${import_paths_env}" + $<TARGET_FILE:qmlplugindump> ${qmltypes_command_args} > "${source_dir}/plugins.qmltypes" + ) +endfunction() + +function(add_qml_module target) + qt_parse_all_arguments(arg "add_qml_module" + "" + "TARGET_PATH;IMPORT_VERSION;IMPORT_NAME;CXX_MODULE;QML_PLUGINDUMP_DENDENCIES" + "QML_FILES" ${ARGN}) + + if (NOT arg_TARGET_PATH) + message(FATAL_ERROR "add_qml_module called without a TARGET_PATH.") + endif() + + qt_add_qmltypes_target(${target} + TARGET_PATH "${arg_TARGET_PATH}" + IMPORT_VERSION "${arg_IMPORT_VERSION}" + IMPORT_NAME "${arg_IMPORT_NAME}" + CXX_MODULE "${arg_CXX_MODULE}" + QML_PLUGINDUMP_DENDENCIES "${arg_QML_PLUGINDUMP_DENDENCIES}") + + set(plugin_types "${CMAKE_CURRENT_SOURCE_DIR}/plugins.qmltypes") + if (EXISTS ${plugin_types}) + qt_copy_or_install(FILES ${plugin_types} + DESTINATION "${INSTALL_QMLDIR}/${arg_TARGET_PATH}" + ) + endif() + + qt_copy_or_install( + FILES + "${CMAKE_CURRENT_SOURCE_DIR}/qmldir" + DESTINATION + "${INSTALL_QMLDIR}/${arg_TARGET_PATH}" + ) + + if(NOT QT_BUILD_SHARED_LIBS) + string(REPLACE "." "_" uri_target ${arg_TARGET_PATH}) + add_qt_resource(${target} ${uri_target} + FILES ${arg_QML_FILES} "${CMAKE_CURRENT_SOURCE_DIR}/qmldir" + PREFIX "/qt-project.org/import/${arg_TARGET_PATH}") + else() + if(arg_QML_FILES) + qt_copy_or_install(FILES ${arg_QML_FILES} + DESTINATION "${INSTALL_QMLDIR}/${arg_TARGET_PATH}" + ) + endif() + endif() + +endfunction() # This function creates a CMake target for a generic console or GUI binary. # Please consider to use a more specific version target like the one created @@ -1910,6 +2048,7 @@ function(add_qt_resource target resourceName) # <file ...>...</file> string(APPEND qrcContents " <file alias=\"${alias}\">") string(APPEND qrcContents "${CMAKE_CURRENT_SOURCE_DIR}/${based_file}</file>\n") + list(APPEND files "${CMAKE_CURRENT_SOURCE_DIR}/${based_file}") endforeach() # </qresource></RCC> diff --git a/util/cmake/helper.py b/util/cmake/helper.py index 3be4a505a7..fcd38883c3 100644 --- a/util/cmake/helper.py +++ b/util/cmake/helper.py @@ -176,6 +176,8 @@ _qt_library_map = [ LibraryMapping('xkbcommon_support', 'Qt6', 'Qt::XkbCommonSupport', extra = ['COMPONENTS', 'XkbCommonSupport']), LibraryMapping('xmlpatterns', 'Qt6', 'Qt::XmlPatterns', extra = ['COMPONENTS', 'XmlPatterns']), LibraryMapping('xml', 'Qt6', 'Qt::Xml', extra = ['COMPONENTS', 'Xml']), + LibraryMapping('qmlworkerscript', 'Qt6', 'Qt::QmlWorkerScript', extra = ['COMPONENTS', 'QmlWorkerScript']), + LibraryMapping('quickparticles', 'Qt6', 'Qt::QuickParticles', extra = ['COMPONENTS', 'QuickParticles']) # qtzlib: No longer supported. ] diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 4e128c4c15..495c0e14ce 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -1441,6 +1441,7 @@ def recursive_evaluate_scope(scope: Scope, parent_condition: str = '', def map_to_cmake_condition(condition: typing.Optional[str]) -> str: + condition = condition.replace("QTDIR_build", "QT_BUILDING_QT") condition = re.sub(r'\bQT_ARCH___equals___([a-zA-Z_0-9]*)', r'(TEST_architecture_arch STREQUAL "\1")', condition or '') condition = re.sub(r'\bQT_ARCH___contains___([a-zA-Z_0-9]*)', @@ -1490,7 +1491,6 @@ def write_extend_target(cm_fh: typing.IO[str], target: str, write_resources(cm_fh, target, scope, indent) - def flatten_scopes(scope: Scope) -> typing.List[Scope]: result = [scope] # type: typing.List[Scope] for c in scope.children: @@ -1770,8 +1770,14 @@ def write_plugin(cm_fh, scope, *, indent: int = 0): extra = [] plugin_type = scope.get_string('PLUGIN_TYPE') + is_qml_plugin = any('qml_plugin' == s for s in scope.get('_LOADED')) + target_path = scope.get_string('TARGETPATH') + if plugin_type: extra.append('TYPE {}'.format(plugin_type)) + elif is_qml_plugin: + extra.append('TYPE {}'.format('qml_plugin')) + extra.append('QML_TARGET_PATH "{}"'.format(target_path)) plugin_class_name = scope.get_string('PLUGIN_CLASS_NAME') if plugin_class_name: @@ -1780,13 +1786,56 @@ def write_plugin(cm_fh, scope, *, indent: int = 0): write_main_part(cm_fh, plugin_name, 'Plugin', 'add_qt_plugin', scope, indent=indent, extra_lines=extra, known_libraries={}, extra_keys=[]) + if is_qml_plugin: + extra = [] + extra.append('TARGET_PATH "{}"'.format(target_path)) + + write_qml_plugin(cm_fh, plugin_name, scope, indent=indent, extra_lines=extra) + + +def write_qml_plugin(cm_fh: typing.IO[str], + target: str, + scope: Scope, *, + extra_lines: typing.List[str] = [], + indent: int = 0, + **kwargs: typing.Any): + # Collect other args if available + cxx_module = scope.get_string('CXX_MODULE') + if cxx_module: + extra_lines.append('CXX_MODULE "{}"'.format(cxx_module)) + import_version = scope.get_string('IMPORT_VERSION') + if import_version: + import_version = import_version.replace("$$QT_MINOR_VERSION","${CMAKE_PROJECT_VERSION_MINOR}") + extra_lines.append('IMPORT_VERSION "{}"'.format(import_version)) + import_name = scope.get_string('IMPORT_NAME') + if import_name: + extra_lines.append('IMPORT_NAME "{}"'.format(import_name)) + plugindump_dep = scope.get_string('QML_PLUGINDUMP_DEPENDENCIES') + if plugindump_dep: + extra_lines.append('QML_PLUGINDUMP_DEPENDENCIES "{}"'.format(plugindump_dep)) + + cm_fh.write('\n{}{}({}\n'.format(spaces(indent), 'add_qml_module', target)) + indent += 1 + for extra_line in extra_lines: + cm_fh.write('{}{}\n'.format(spaces(indent), extra_line)) + + qml_files = scope.expand('QML_FILES') + if qml_files: + cm_fh.write('{}{}\n'.format(spaces(indent), 'QML_FILES')) + write_list(cm_fh, qml_files, '', indent=indent + 1) + + # Footer: + indent -= 1 + cm_fh.write('{})\n'.format(spaces(indent))) + def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *, indent: int = 0, is_example: bool=False) -> None: assert scope.TEMPLATE in ('app', 'lib') is_lib = scope.TEMPLATE == 'lib' - is_plugin = any('qt_plugin' == s for s in scope.get('_LOADED')) + is_qml_plugin = any('qml_plugin' == s for s in scope.get('_LOADED')) + is_plugin = any('qt_plugin' == s for s in scope.get('_LOADED')) or is_qml_plugin if is_lib or 'qt_module' in scope.get('_LOADED'): assert not is_example |