diff options
-rw-r--r-- | modules/ECMQmlModule.cmake | 419 | ||||
-rw-r--r-- | modules/ECMQmlModule.cpp.in | 12 | ||||
-rw-r--r-- | modules/ECMQmlModule.cpp.in.license | 2 | ||||
-rw-r--r-- | modules/ECMQmlModule.h.in | 17 | ||||
-rw-r--r-- | modules/ECMQmlModule.h.in.license | 2 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 44 | ||||
-rw-r--r-- | tests/ECMQmlModuleTest/CMakeLists.txt | 32 | ||||
-rw-r--r-- | tests/ECMQmlModuleTest/QmlModule.qml | 11 | ||||
-rw-r--r-- | tests/ECMQmlModuleTest/check.cmake.in | 64 | ||||
-rw-r--r-- | tests/ECMQmlModuleTest/qmldir_expected_depends | 6 | ||||
-rw-r--r-- | tests/ECMQmlModuleTest/qmldir_expected_depends.license | 2 | ||||
-rw-r--r-- | tests/ECMQmlModuleTest/qmldir_expected_full | 5 | ||||
-rw-r--r-- | tests/ECMQmlModuleTest/qmldir_expected_full.license | 2 | ||||
-rw-r--r-- | tests/ECMQmlModuleTest/qmldir_expected_qmlonly | 3 | ||||
-rw-r--r-- | tests/ECMQmlModuleTest/qmldir_expected_qmlonly.license | 2 | ||||
-rw-r--r-- | tests/ECMQmlModuleTest/qmlmodule.cpp | 14 | ||||
-rw-r--r-- | tests/ECMQmlModuleTest/qmlmodule.h | 21 |
17 files changed, 658 insertions, 0 deletions
diff --git a/modules/ECMQmlModule.cmake b/modules/ECMQmlModule.cmake new file mode 100644 index 00000000..75bc464a --- /dev/null +++ b/modules/ECMQmlModule.cmake @@ -0,0 +1,419 @@ +# +# SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl> +# +# SPDX-License-Identifier: BSD-3-Clause + +#[========================================================================[.rst: +ECMQmlModule +------------ + +This file contains helper functions to make it easier to create QML modules. It +takes care of a number of things that often need to be repeated. It also takes +care of special handling of QML modules between shared and static builds. When +building a static version of a QML module, the relevant QML source files are +bundled into the static library. When using a shared build, the QML plugin and +relevant QML files are copied to the target's RUNTIME_OUTPUT_DIRECTORY to make +it easier to run things directly from the build directory. + +Example usage: + +.. code-block:: cmake + + ecm_add_qml_module(ExampleModule URI "org.example.Example" VERSION 1.4) + + target_sources(ExampleModule PRIVATE ExamplePlugin.cpp) + target_link_libraries(ExampleModule PRIVATE Qt::Quick) + + ecm_target_qml_sources(ExampleModule SOURCES ExampleItem.qml) + ecm_target_qml_sources(ExampleModule SOURCES AnotherExampleItem.qml VERSION 1.5) + + ecm_finalize_qml_module(ExampleModule DESTINATION ${KDE_INSTALL_QMLDIR}) + + +:: + ecm_add_qml_module(<target name> URI <module uri> [VERSION <module version>] [NO_PLUGIN] [CLASSNAME <class name>]) + +This will declare a new CMake target called ``<target name>``. The ``URI`` +argument is required and should be a proper QML module URI. The URI is used, +among others, to generate a subdirectory where the module will be installed to. + +If the ``VERSION`` argument is specified, it is used to initialize the default +version that is used by ``ecm_target_qml_sources`` when adding QML files. If it +is not specified, a default of 1.0 is used. Additionally, if a version greater +than or equal to 2.0 is specified, the major version is appended to the +installation path of the module. + +If the option ``NO_PLUGIN`` is set, a target is declared that is not expected to +contain any C++ QML plugin. + +If the optional ``CLASSNAME`` argument is supplied, it will be used as class +name in the generated QMLDIR file. If it is not specified, the target name will +be used instead. + +You can add C++ and QML source files to the target using ``target_sources`` and +``ecm_target_qml_sources``, respectively. + +Since 5.87.0 + +:: + ecm_add_qml_module_dependencies(<target> DEPENDS <module string> [<module string> ...]) + +Add the list of dependencies specified by the ``DEPENDS`` argument to be listed +as dependencies in the generated QMLDIR file of ``<target>``. + +Since 5.87.0 + +:: + ecm_target_qml_sources(<target> SOURCES <source.qml> [<source.qml> ...] [VERSION <version>] [PATH <path>] [PRIVATE]) + +Add the list of QML files specified by the ``SOURCES`` argument as source files +to the QML module target ``<target>``. + +If the optional ``VERSION`` argument is specified, all QML files will be added +with the specified version. If it is not specified, they will use the version of +the QML module target. + +If the optional ``PRIVATE`` argument is specified, the QML files will be +included in the target but not in the generated qmldir file. Any version +argument will be ignored. + +The optional ``PATH`` argument declares a subdirectory of the module where the +files should be copied to. By default, files will be copied to the module root. + +This function will fail if ``<target>`` is not a QML module target or any of the +specified files do not exist. + +Since 5.87.0 + +:: + ecm_finalize_qml_module(<target> DESTINATION <QML install destination>) + +Finalize the specified QML module target. This must be called after all other +setup (like adding sources) on the target has been done. It will perform a +number of tasks: + +- It will generate a qmldir file from the QML files added to the target. If the + module has a C++ plugin, this will also be included in the qmldir file. +- If ``BUILD_SHARED_LIBS`` is off, a QRC file is generated from the QML files + added to the target. This QRC file will be included when compiling the C++ QML + module. The built static library will be installed in a subdirection of + ``DESTINATION`` based on the QML module's uri. Note that if ``NO_PLUGIN`` is + set, a C++ QML plugin will be generated to include the QRC files. +- If ``BUILD_SHARED_LIBS`` in on, all generated files, QML sources and the C++ + plugin will be installed in a subdirectory of ``DESTINATION`` based upon the + QML module's uri. In addition, these files will also be copied to the target's + ``RUNTIME_OUTPUT_DIRECTORY`` in a similar subdirectory. + +This function will fail if ``<target>`` is not a QML module target. + +Since 5.87.0 + +#]========================================================================] + +include(CMakeParseArguments) + +set(_ECM_QMLMODULE_STATIC_QMLONLY_H "${CMAKE_CURRENT_LIST_DIR}/ECMQmlModule.h.in") +set(_ECM_QMLMODULE_STATIC_QMLONLY_CPP "${CMAKE_CURRENT_LIST_DIR}/ECMQmlModule.cpp.in") + +set(_ECM_QMLMODULE_PROPERTY_URI "_ecm_qml_uri") +set(_ECM_QMLMODULE_PROPERTY_QMLDIR "_ecm_qmldir_file") +set(_ECM_QMLMODULE_PROPERTY_FILES "_ecm_qml_files") +set(_ECM_QMLMODULE_PROPERTY_QMLONLY "_ecm_qml_only") +set(_ECM_QMLMODULE_PROPERTY_VERSION "_ecm_qml_version") +set(_ECM_QMLMODULE_PROPERTY_PRIVATE "_ecm_qml_private") +set(_ECM_QMLMODULE_PROPERTY_PATH "_ecm_qml_path") +set(_ECM_QMLMODULE_PROPERTY_CLASSNAME "_ecm_qml_classname") +set(_ECM_QMLMODULE_PROPERTY_DEPENDS "_ecm_qml_depends") + +macro(_ecm_qmlmodule_verify_qml_target ARG_TARGET) + if (NOT TARGET ${ARG_TARGET}) + message(FATAL_ERROR "Target ${ARG_TARGET} does not exist") + endif() + get_target_property(_qml_uri ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_URI}) + if ("${_qml_uri}" STREQUAL "" OR "${_qml_uri}" STREQUAL "${_ECM_QMLMODULE_PROPERTY_URI}-NOTFOUND") + message(FATAL_ERROR "Target ${ARG_TARGET} is not a QML plugin target") + endif() +endmacro() + +function(_ecm_qmlmodule_generate_qmldir ARG_TARGET) + get_target_property(_qml_uri ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_URI}) + get_target_property(_qml_files ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_FILES}) + get_target_property(_qml_only ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_QMLONLY}) + get_target_property(_qml_classname ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_CLASSNAME}) + + set(_qmldir_template "# This file was automatically generated by ECMQmlModule and should not be modified") + + string(APPEND _qmldir_template "\nmodule ${_qml_uri}") + + if (NOT ${_qml_only}) + string(APPEND _qmldir_template "\nplugin ${ARG_TARGET}") + + if ("${_qml_classname}" STREQUAL "") + string(APPEND _qmldir_template "\nclassname ${ARG_TARGET}") + else() + string(APPEND _qmldir_template "\nclassname ${_qml_classname}") + endif() + endif() + + get_target_property(_qml_depends ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_DEPENDS}) + if (NOT "${_qml_depends}" STREQUAL "") + foreach(_depends ${_qml_depends}) + string(APPEND _qmldir_template "\ndepends ${_depends}") + endforeach() + endif() + + foreach(_file ${_qml_files}) + get_filename_component(_filename ${_file} NAME) + get_filename_component(_classname ${_file} NAME_WE) + get_property(_version SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_VERSION}) + get_property(_private SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PRIVATE}) + if (NOT _private) + string(APPEND _qmldir_template "\n${_classname} ${_version} ${_filename}") + endif() + endforeach() + + string(APPEND _qmldir_template "\n") + + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}_qmldir" "${_qmldir_template}") + set_target_properties(${ARG_TARGET} PROPERTIES _ecm_qmldir_file "${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}_qmldir") +endfunction() + +function(_ecm_qmlmodule_generate_qrc ARG_TARGET) + get_target_property(_qml_uri ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_URI}) + get_target_property(_qml_files ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_FILES}) + get_target_property(_qmldir_file ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_QMLDIR}) + + string(REPLACE "." "/" _qml_prefix ${_qml_uri}) + + set(_qrc_template "<!-- This file was automatically generated by ECMQmlModule and should not be modified -->") + + string(APPEND _qrc_template "\n<RCC>\n<qresource prefix=\"${_qml_prefix}\">") + string(APPEND _qrc_template "\n<file alias=\"qmldir\">${_qmldir_file}</file>") + + foreach(_file ${_qml_files}) + get_filename_component(_filename ${_file} NAME) + get_property(_path SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PATH}) + + set(_file_path "${_filename}") + if (NOT "${_path}" STREQUAL "") + set(_file_path "${_path}/${_filename}") + endif() + + string(APPEND _qrc_template "\n<file alias=\"${_file_path}\">${_file}</file>") + endforeach() + + string(APPEND _qrc_template "\n</qresource>\n</RCC>\n") + + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}.qrc" "${_qrc_template}") + qt_add_resources(_qrc_output "${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}.qrc") + + target_sources(${ARG_TARGET} PRIVATE ${_qrc_output}) +endfunction() + +function(ecm_add_qml_module ARG_TARGET) + cmake_parse_arguments(PARSE_ARGV 1 ARG "NO_PLUGIN" "URI;VERSION;CLASSNAME" "") + + if ("${ARG_TARGET}" STREQUAL "") + message(FATAL_ERROR "ecm_add_qml_module called without a valid target name.") + endif() + + if ("${ARG_URI}" STREQUAL "") + message(FATAL_ERROR "ecm_add_qml_module called without a valid module URI.") + endif() + + string(FIND "${ARG_URI}" " " "_space") + if (${_space} GREATER_EQUAL 0) + message(FATAL_ERROR "ecm_add_qml_module called without a valid module URI.") + endif() + + if (${BUILD_SHARED_LIBS} AND ${ARG_NO_PLUGIN}) + # CMake doesn't like library targets without sources, so if we have no + # C++ sources, use a plain target that we can add all the install stuff + # to. + add_custom_target(${ARG_TARGET} ALL) + else() + add_library(${ARG_TARGET}) + endif() + + if ("${ARG_VERSION}" STREQUAL "") + set(ARG_VERSION "1.0") + endif() + + set_target_properties(${ARG_TARGET} PROPERTIES + ${_ECM_QMLMODULE_PROPERTY_URI} "${ARG_URI}" + ${_ECM_QMLMODULE_PROPERTY_FILES} "" + ${_ECM_QMLMODULE_PROPERTY_QMLONLY} "${ARG_NO_PLUGIN}" + ${_ECM_QMLMODULE_PROPERTY_VERSION} "${ARG_VERSION}" + ${_ECM_QMLMODULE_PROPERTY_CLASSNAME} "${ARG_CLASSNAME}" + ${_ECM_QMLMODULE_PROPERTY_DEPENDS} "" + ) +endfunction() + +function(ecm_add_qml_module_dependencies ARG_TARGET) + cmake_parse_arguments(PARSE_ARGV 1 ARG "" "" "DEPENDS") + + _ecm_qmlmodule_verify_qml_target(${ARG_TARGET}) + + if ("${ARG_DEPENDS}" STREQUAL "") + message(FATAL_ERROR "ecm_add_qml_module_dependencies called without required argument DEPENDS") + endif() + + set_target_properties(${ARG_TARGET} PROPERTIES ${_ECM_QMLMODULE_PROPERTY_DEPENDS} "${ARG_DEPENDS}") +endfunction() + +function(ecm_target_qml_sources ARG_TARGET) + cmake_parse_arguments(PARSE_ARGV 1 ARG "PRIVATE" "VERSION;PATH" "SOURCES") + + _ecm_qmlmodule_verify_qml_target(${ARG_TARGET}) + + if ("${ARG_SOURCES}" STREQUAL "") + message(FATAL_ERROR "ecm_target_qml_sources called without required argument SOURCES") + endif() + + if ("${ARG_VERSION}" STREQUAL "") + get_target_property(ARG_VERSION ${ARG_TARGET} "_ecm_qml_version") + endif() + + foreach(_file ${ARG_SOURCES}) + # Check if a given file exists, but only for files that are not + # automatically generated. + set(_path "${_file}") + get_source_file_property(_generated ${_file} GENERATED) + if (NOT _generated) + if (IS_ABSOLUTE ${_path}) + # Assume absolute paths are generated, which may not always be + # true but is fairly likely. + set(_generated TRUE) + else() + string(FIND ${_file} ${CMAKE_BINARY_DIR} _in_binary_dir) + if (${_in_binary_dir} GREATER_EQUAL 0) + # Assume anything in binary dir is generated. + set(_generated TRUE) + else() + set(_path "${CMAKE_CURRENT_SOURCE_DIR}/${_file}") + endif() + endif() + endif() + + if (NOT ${_generated} AND NOT EXISTS ${_path}) + message(FATAL_ERROR "ecm_target_qml_sources called with nonexistent file ${_file}") + endif() + + if (NOT ARG_PRIVATE) + set_property(SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_VERSION} "${ARG_VERSION}") + else() + set_property(SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PRIVATE} TRUE) + endif() + set_property(SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PATH} "${ARG_PATH}") + set_property(TARGET ${ARG_TARGET} + APPEND PROPERTY ${_ECM_QMLMODULE_PROPERTY_FILES} ${_path} + ) + endforeach() +endfunction() + +function(ecm_finalize_qml_module ARG_TARGET) + cmake_parse_arguments(PARSE_ARGV 1 ARG "" "DESTINATION" "") + + _ecm_qmlmodule_verify_qml_target(${ARG_TARGET}) + + if ("${ARG_DESTINATION}" STREQUAL "") + message(FATAL_ERROR "ecm_finalize_qml_module called without required argument DESTINATION") + endif() + + _ecm_qmlmodule_generate_qmldir(${ARG_TARGET}) + + get_target_property(_qml_uri ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_URI}) + get_target_property(_version ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_VERSION}) + + string(REPLACE "." "/" _plugin_path "${_qml_uri}") + + if (${_version} VERSION_GREATER_EQUAL 2.0) + string(REGEX MATCH "^([0-9]+)" _version_major ${_version}) + set(_plugin_path "${_plugin_path}.${_version_major}") + endif() + + get_target_property(_qml_only ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_QMLONLY}) + + if (NOT BUILD_SHARED_LIBS) + _ecm_qmlmodule_generate_qrc(${ARG_TARGET}) + + if (${_qml_only}) + # If we do not have any C++ sources for the target, we need a way to + # compile the generated QRC file. So generate a very plain C++ QML + # plugin that we include in the target. + configure_file(${_ECM_QMLMODULE_STATIC_QMLONLY_H} ${CMAKE_CURRENT_BINARY_DIR}/QmlModule.h @ONLY) + configure_file(${_ECM_QMLMODULE_STATIC_QMLONLY_CPP} ${CMAKE_CURRENT_BINARY_DIR}/QmlModule.cpp @ONLY) + + target_sources(${ARG_TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/QmlModule.cpp) + if (TARGET Qt::Qml) + target_link_libraries(${ARG_TARGET} PRIVATE Qt::Qml) + else() + target_link_libraries(${ARG_TARGET} PRIVATE Qt5::Qml) + endif() + endif() + + install(TARGETS ${ARG_TARGET} DESTINATION ${ARG_DESTINATION}/${_plugin_path}) + + return() + endif() + + get_target_property(_runtime_output_dir ${ARG_TARGET} RUNTIME_OUTPUT_DIRECTORY) + if (NOT ${_runtime_output_dir}) + if ("${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" STREQUAL "") + set(_runtime_output_dir ${CMAKE_CURRENT_BINARY_DIR}) + else() + set(_runtime_output_dir ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + endif() + endif() + + add_custom_command( + TARGET ${ARG_TARGET} + POST_BUILD + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E make_directory ${_runtime_output_dir}/${_plugin_path} + BYPRODUCTS ${_runtime_output_dir}/${_plugin_path} + ) + + get_target_property(_qmldir_file ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_QMLDIR}) + install(FILES ${_qmldir_file} DESTINATION ${ARG_DESTINATION}/${_plugin_path} RENAME "qmldir") + + add_custom_command( + TARGET ${ARG_TARGET} + POST_BUILD + WORKING_DIRECTORY ${_runtime_output_dir} + COMMAND ${CMAKE_COMMAND} -E copy ${_qmldir_file} ${_plugin_path}/qmldir + ) + + if (NOT ${_qml_only}) + install(TARGETS ${ARG_TARGET} DESTINATION ${ARG_DESTINATION}/${_plugin_path}) + + add_custom_command( + TARGET ${ARG_TARGET} + POST_BUILD + WORKING_DIRECTORY ${_runtime_output_dir} + COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${ARG_TARGET}> ${_plugin_path} + ) + endif() + + get_target_property(_qml_files ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_FILES}) + foreach(_file ${_qml_files}) + get_filename_component(_filename ${_file} NAME) + get_property(_path SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PATH}) + + set(_file_path "${_plugin_path}/") + if (NOT "${_path}" STREQUAL "") + set(_file_path "${_plugin_path}/${_path}/") + endif() + + install(FILES ${_file} DESTINATION ${ARG_DESTINATION}/${_file_path}) + + add_custom_command( + TARGET ${ARG_TARGET} + POST_BUILD + WORKING_DIRECTORY ${_runtime_output_dir} + COMMAND ${CMAKE_COMMAND} -E make_directory ${_file_path} + COMMAND ${CMAKE_COMMAND} -E copy ${_file} ${_file_path} + BYPRODUCTS ${_file_path}/${_filename} + ) + endforeach() +endfunction() diff --git a/modules/ECMQmlModule.cpp.in b/modules/ECMQmlModule.cpp.in new file mode 100644 index 00000000..07be0bc3 --- /dev/null +++ b/modules/ECMQmlModule.cpp.in @@ -0,0 +1,12 @@ +// This file is autogenerated by ECMQmlModule to support static QML-only plugins. + +#include "QmlModule.h" + +#include <QQmlEngine> + +void QmlModule::registerTypes(const char* uri) +{ + Q_ASSERT(QLatin1String(uri) == QLatin1String("@_qml_uri@")); + + Q_INIT_RESOURCE(@ARG_TARGET@); +} diff --git a/modules/ECMQmlModule.cpp.in.license b/modules/ECMQmlModule.cpp.in.license new file mode 100644 index 00000000..8ebd85e2 --- /dev/null +++ b/modules/ECMQmlModule.cpp.in.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl> +SPDX-License-Identifier: CC0 diff --git a/modules/ECMQmlModule.h.in b/modules/ECMQmlModule.h.in new file mode 100644 index 00000000..6dd830de --- /dev/null +++ b/modules/ECMQmlModule.h.in @@ -0,0 +1,17 @@ +// This file is autogenerated by ECMQmlModule to support static QML-only plugins. + +#ifndef QMLMODULE_H +#define QMLMODULE_H + +#include <QQmlExtensionPlugin> + +class QmlModule : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") + +public: + void registerTypes(const char* uri) override; +}; + +#endif diff --git a/modules/ECMQmlModule.h.in.license b/modules/ECMQmlModule.h.in.license new file mode 100644 index 00000000..8ebd85e2 --- /dev/null +++ b/modules/ECMQmlModule.h.in.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl> +SPDX-License-Identifier: CC0 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b2143ec9..7ffcfd03 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -209,6 +209,50 @@ set_package_properties( ) if (TARGET Qt5::Quick) add_test_macro(ECMQMLModules dummy) + + set(ECMQmlModuleTest.static_full_EXTRA_OPTIONS + --build-target install + --build-options -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/ECMQmlModuleTest/static_full/install + -DBUILD_SHARED_LIBS=OFF + ) + add_test_variant(ECMQmlModuleTest.static_full ECMQmlModuleTest + ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/ECMQmlModuleTest/static_full/check.cmake" + ) + set(ECMQmlModuleTest.shared_full_EXTRA_OPTIONS + --build-target install + --build-options -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/ECMQmlModuleTest/shared_full/install + -DBUILD_SHARED_LIBS=ON + ) + add_test_variant(ECMQmlModuleTest.shared_full ECMQmlModuleTest + ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/ECMQmlModuleTest/shared_full/check.cmake" + ) + set(ECMQmlModuleTest.static_qmlonly_EXTRA_OPTIONS + --build-target install + --build-options -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/ECMQmlModuleTest/static_qmlonly/install + -DBUILD_SHARED_LIBS=OFF + -DQML_ONLY=ON + ) + add_test_variant(ECMQmlModuleTest.static_qmlonly ECMQmlModuleTest + ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/ECMQmlModuleTest/static_qmlonly/check.cmake" + ) + set(ECMQmlModuleTest.shared_qmlonly_EXTRA_OPTIONS + --build-target install + --build-options -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/ECMQmlModuleTest/shared_qmlonly/install + -DBUILD_SHARED_LIBS=ON + -DQML_ONLY=ON + ) + add_test_variant(ECMQmlModuleTest.shared_qmlonly ECMQmlModuleTest + ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/ECMQmlModuleTest/shared_qmlonly/check.cmake" + ) + set(ECMQmlModuleTest.shared_depends_EXTRA_OPTIONS + --build-target install + --build-options -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/ECMQmlModuleTest/shared_depends/install + -DBUILD_SHARED_LIBS=ON + -DDEPENDS=ON + ) + add_test_variant(ECMQmlModuleTest.shared_depends ECMQmlModuleTest + ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/ECMQmlModuleTest/shared_depends/check.cmake" + ) endif() set(ECMConfiguredInstallTest_EXTRA_OPTIONS diff --git a/tests/ECMQmlModuleTest/CMakeLists.txt b/tests/ECMQmlModuleTest/CMakeLists.txt new file mode 100644 index 00000000..49d76594 --- /dev/null +++ b/tests/ECMQmlModuleTest/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl> +# +# SPDX-License-Identifier: BSD-3-Clause + +project(extra-cmake-modules) +cmake_minimum_required(VERSION 3.5) + +set(ECM_MODULE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../modules) +set(CMAKE_MODULE_PATH "${ECM_FIND_MODULE_DIR}" "${ECM_MODULE_DIR}") + +find_package(Qt5 REQUIRED COMPONENTS Qml) +include(ECMQmlModule) + +if(QML_ONLY) + ecm_add_qml_module(TestModule URI Test NO_PLUGIN) +else() + ecm_add_qml_module(TestModule URI Test) + target_sources(TestModule PRIVATE qmlmodule.cpp) + target_link_libraries(TestModule Qt5::Qml) +endif() + +if (DEPENDS) + ecm_add_qml_module_dependencies(TestModule DEPENDS OtherTest) +endif() + +ecm_target_qml_sources(TestModule SOURCES QmlModule.qml) + +ecm_finalize_qml_module(TestModule DESTINATION "test") + +# this will be run by CTest +configure_file(check.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/check.cmake" @ONLY) diff --git a/tests/ECMQmlModuleTest/QmlModule.qml b/tests/ECMQmlModuleTest/QmlModule.qml new file mode 100644 index 00000000..ce16c9db --- /dev/null +++ b/tests/ECMQmlModuleTest/QmlModule.qml @@ -0,0 +1,11 @@ +/** + * SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl> + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +import QtQuick 2.15 + +Item { + +} diff --git a/tests/ECMQmlModuleTest/check.cmake.in b/tests/ECMQmlModuleTest/check.cmake.in new file mode 100644 index 00000000..af64a120 --- /dev/null +++ b/tests/ECMQmlModuleTest/check.cmake.in @@ -0,0 +1,64 @@ +# +# SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl> +# +# SPDX-License-Identifier: BSD-3-Clause + +set(SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@") +set(INSTALL_DIR "@CMAKE_INSTALL_PREFIX@/test") +set(SHARED "@BUILD_SHARED_LIBS@") +set(QML_ONLY "@QML_ONLY@") +set(DEPENDS "@DEPENDS@") + +function(check_file_exists file) + if (NOT EXISTS ${file}) + message(FATAL_ERROR "File \"${file}\" does not exist") + endif() +endfunction() + +function (check_file_contents) + cmake_parse_arguments(ARGS "" "GENERATED;EXPECTED" "" ${ARGN}) + + if (NOT EXISTS "${ARGS_GENERATED}") + message(FATAL_ERROR "${ARGS_GENERATED} was not generated") + endif() + file(READ "${ARGS_GENERATED}" generated_contents) + if (NOT EXISTS "${ARGS_EXPECTED}") + message(FATAL_ERROR "Original ${ARGS_EXPECTED} was not found") + endif() + file(READ "${ARGS_EXPECTED}" original_contents) + if (NOT "${generated_contents}" STREQUAL "${original_contents}") + message(FATAL_ERROR "${generated_file} contains '${generated_contents}' instead of '${original_contents}'") + endif() +endfunction() + +if (SHARED) + check_file_contents( + GENERATED "${INSTALL_DIR}/Test/QmlModule.qml" + EXPECTED "${SOURCE_DIR}/QmlModule.qml" + ) + + if (NOT QML_ONLY AND NOT DEPENDS) + check_file_exists("${INSTALL_DIR}/Test/libTestModule.so") + + check_file_contents( + GENERATED "${INSTALL_DIR}/Test/qmldir" + EXPECTED "${SOURCE_DIR}/qmldir_expected_full" + ) + endif() + + if (QML_ONLY AND NOT DEPENDS) + check_file_contents( + GENERATED "${INSTALL_DIR}/Test/qmldir" + EXPECTED "${SOURCE_DIR}/qmldir_expected_qmlonly" + ) + endif() + + if (DEPENDS) + check_file_contents( + GENERATED "${INSTALL_DIR}/Test/qmldir" + EXPECTED "${SOURCE_DIR}/qmldir_expected_depends" + ) + endif() +else() + check_file_exists("${INSTALL_DIR}/Test/libTestModule.a") +endif() diff --git a/tests/ECMQmlModuleTest/qmldir_expected_depends b/tests/ECMQmlModuleTest/qmldir_expected_depends new file mode 100644 index 00000000..2f373e2f --- /dev/null +++ b/tests/ECMQmlModuleTest/qmldir_expected_depends @@ -0,0 +1,6 @@ +# This file was automatically generated by ECMQmlModule and should not be modified +module Test +plugin TestModule +classname TestModule +depends OtherTest +QmlModule 1.0 QmlModule.qml diff --git a/tests/ECMQmlModuleTest/qmldir_expected_depends.license b/tests/ECMQmlModuleTest/qmldir_expected_depends.license new file mode 100644 index 00000000..8ebd85e2 --- /dev/null +++ b/tests/ECMQmlModuleTest/qmldir_expected_depends.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl> +SPDX-License-Identifier: CC0 diff --git a/tests/ECMQmlModuleTest/qmldir_expected_full b/tests/ECMQmlModuleTest/qmldir_expected_full new file mode 100644 index 00000000..9a13246c --- /dev/null +++ b/tests/ECMQmlModuleTest/qmldir_expected_full @@ -0,0 +1,5 @@ +# This file was automatically generated by ECMQmlModule and should not be modified +module Test +plugin TestModule +classname TestModule +QmlModule 1.0 QmlModule.qml diff --git a/tests/ECMQmlModuleTest/qmldir_expected_full.license b/tests/ECMQmlModuleTest/qmldir_expected_full.license new file mode 100644 index 00000000..8ebd85e2 --- /dev/null +++ b/tests/ECMQmlModuleTest/qmldir_expected_full.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl> +SPDX-License-Identifier: CC0 diff --git a/tests/ECMQmlModuleTest/qmldir_expected_qmlonly b/tests/ECMQmlModuleTest/qmldir_expected_qmlonly new file mode 100644 index 00000000..01823063 --- /dev/null +++ b/tests/ECMQmlModuleTest/qmldir_expected_qmlonly @@ -0,0 +1,3 @@ +# This file was automatically generated by ECMQmlModule and should not be modified +module Test +QmlModule 1.0 QmlModule.qml diff --git a/tests/ECMQmlModuleTest/qmldir_expected_qmlonly.license b/tests/ECMQmlModuleTest/qmldir_expected_qmlonly.license new file mode 100644 index 00000000..8ebd85e2 --- /dev/null +++ b/tests/ECMQmlModuleTest/qmldir_expected_qmlonly.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl> +SPDX-License-Identifier: CC0 diff --git a/tests/ECMQmlModuleTest/qmlmodule.cpp b/tests/ECMQmlModuleTest/qmlmodule.cpp new file mode 100644 index 00000000..a1552a86 --- /dev/null +++ b/tests/ECMQmlModuleTest/qmlmodule.cpp @@ -0,0 +1,14 @@ +/** + * SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl> + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "qmlmodule.h" + +#include <QtQml/QQmlEngine> + +void QmlModule::registerTypes(const char* uri) +{ + Q_ASSERT(QLatin1String(uri) == QLatin1String("Test")); +} diff --git a/tests/ECMQmlModuleTest/qmlmodule.h b/tests/ECMQmlModuleTest/qmlmodule.h new file mode 100644 index 00000000..49cd83a9 --- /dev/null +++ b/tests/ECMQmlModuleTest/qmlmodule.h @@ -0,0 +1,21 @@ +/** + * SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl> + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef QMLMODULE_H +#define QMLMODULE_H + +#include <QtQml/QQmlExtensionPlugin> + +class QmlModule : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") + +public: + void registerTypes(const char* uri) override; +}; + +#endif |