Warning, /frameworks/extra-cmake-modules/modules/ECMQmlModule.cmake is written in an unsupported language. File is not indexed.
0001 # 0002 # SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl> 0003 # 0004 # SPDX-License-Identifier: BSD-3-Clause 0005 0006 #[========================================================================[.rst: 0007 ECMQmlModule 0008 ------------ 0009 0010 This file contains helper functions to make it easier to create QML modules. It 0011 takes care of a number of things that often need to be repeated. It also takes 0012 care of special handling of QML modules between shared and static builds. When 0013 building a static version of a QML module, the relevant QML source files are 0014 bundled into the static library. When using a shared build, the QML plugin and 0015 relevant QML files are copied to the target's ``RUNTIME_OUTPUT_DIRECTORY`` to make 0016 it easier to run things directly from the build directory. 0017 0018 Example usage: 0019 0020 .. code-block:: cmake 0021 0022 ecm_add_qml_module(ExampleModule URI "org.example.Example" VERSION 1.4) 0023 0024 target_sources(ExampleModule PRIVATE ExamplePlugin.cpp) 0025 target_link_libraries(ExampleModule PRIVATE Qt::Quick) 0026 0027 ecm_target_qml_sources(ExampleModule SOURCES ExampleItem.qml) 0028 ecm_target_qml_sources(ExampleModule SOURCES AnotherExampleItem.qml VERSION 1.5) 0029 0030 ecm_finalize_qml_module(ExampleModule DESTINATION ${KDE_INSTALL_QMLDIR}) 0031 0032 0033 :: 0034 0035 ecm_add_qml_module(<target name> URI <module uri> [VERSION <module version>] [NO_PLUGIN] [CLASSNAME <class name>]) 0036 0037 This will declare a new CMake target called ``<target name>``. The ``URI`` 0038 argument is required and should be a proper QML module URI. The ``URI`` is used, 0039 among others, to generate a subdirectory where the module will be installed to. 0040 0041 If the ``VERSION`` argument is specified, it is used to initialize the default 0042 version that is used by ``ecm_target_qml_sources`` when adding QML files. If it 0043 is not specified, a default of 1.0 is used. Additionally, if a version greater 0044 than or equal to 2.0 is specified, the major version is appended to the 0045 installation path of the module. 0046 0047 If the option ``NO_PLUGIN`` is set, a target is declared that is not expected to 0048 contain any C++ QML plugin. 0049 0050 If the optional ``CLASSNAME`` argument is supplied, it will be used as class 0051 name in the generated QMLDIR file. If it is not specified, the target name will 0052 be used instead. 0053 0054 You can add C++ and QML source files to the target using ``target_sources`` and 0055 ``ecm_target_qml_sources``, respectively. 0056 0057 Since 5.91.0 0058 0059 :: 0060 0061 ecm_add_qml_module_dependencies(<target> DEPENDS <module string> [<module string> ...]) 0062 0063 Add the list of dependencies specified by the ``DEPENDS`` argument to be listed 0064 as dependencies in the generated QMLDIR file of ``<target>``. 0065 0066 Since 5.91.0 0067 0068 :: 0069 0070 ecm_target_qml_sources(<target> SOURCES <source.qml> [<source.qml> ...] [VERSION <version>] [PATH <path>] [PRIVATE]) 0071 0072 Add the list of QML files specified by the ``SOURCES`` argument as source files 0073 to the QML module target ``<target>``. 0074 0075 If the optional ``VERSION`` argument is specified, all QML files will be added 0076 with the specified version. If it is not specified, they will use the version of 0077 the QML module target. 0078 0079 If the optional ``PRIVATE`` argument is specified, the QML files will be 0080 included in the target but not in the generated qmldir file. Any version 0081 argument will be ignored. 0082 0083 The optional ``PATH`` argument declares a subdirectory of the module where the 0084 files should be copied to. By default, files will be copied to the module root. 0085 0086 This function will fail if ``<target>`` is not a QML module target or any of the 0087 specified files do not exist. 0088 0089 Since 5.91.0 0090 0091 :: 0092 0093 ecm_finalize_qml_module(<target> DESTINATION <QML install destination>) 0094 0095 Finalize the specified QML module target. This must be called after all other 0096 setup (like adding sources) on the target has been done. It will perform a 0097 number of tasks: 0098 0099 - It will generate a qmldir file from the QML files added to the target. If the 0100 module has a C++ plugin, this will also be included in the qmldir file. 0101 - If ``BUILD_SHARED_LIBS`` is off, a QRC file is generated from the QML files 0102 added to the target. This QRC file will be included when compiling the C++ QML 0103 module. The built static library will be installed in a subdirection of 0104 ``DESTINATION`` based on the QML module's uri. Note that if ``NO_PLUGIN`` is 0105 set, a C++ QML plugin will be generated to include the QRC files. 0106 - If ``BUILD_SHARED_LIBS`` in on, all generated files, QML sources and the C++ 0107 plugin will be installed in a subdirectory of ``DESTINATION`` based upon the 0108 QML module's uri. In addition, these files will also be copied to the target's 0109 ``RUNTIME_OUTPUT_DIRECTORY`` in a similar subdirectory. 0110 0111 This function will fail if ``<target>`` is not a QML module target. 0112 0113 Since 5.91.0 0114 0115 #]========================================================================] 0116 0117 set(_ECM_QMLMODULE_STATIC_QMLONLY_H "${CMAKE_CURRENT_LIST_DIR}/ECMQmlModule.h.in") 0118 set(_ECM_QMLMODULE_STATIC_QMLONLY_CPP "${CMAKE_CURRENT_LIST_DIR}/ECMQmlModule.cpp.in") 0119 0120 set(_ECM_QMLMODULE_PROPERTY_URI "_ecm_qml_uri") 0121 set(_ECM_QMLMODULE_PROPERTY_QMLDIR "_ecm_qmldir_file") 0122 set(_ECM_QMLMODULE_PROPERTY_FILES "_ecm_qml_files") 0123 set(_ECM_QMLMODULE_PROPERTY_QMLONLY "_ecm_qml_only") 0124 set(_ECM_QMLMODULE_PROPERTY_VERSION "_ecm_qml_version") 0125 set(_ECM_QMLMODULE_PROPERTY_PRIVATE "_ecm_qml_private") 0126 set(_ECM_QMLMODULE_PROPERTY_PATH "_ecm_qml_path") 0127 set(_ECM_QMLMODULE_PROPERTY_CLASSNAME "_ecm_qml_classname") 0128 set(_ECM_QMLMODULE_PROPERTY_DEPENDS "_ecm_qml_depends") 0129 0130 macro(_ecm_qmlmodule_verify_qml_target ARG_TARGET) 0131 if (NOT TARGET ${ARG_TARGET}) 0132 message(FATAL_ERROR "Target ${ARG_TARGET} does not exist") 0133 endif() 0134 get_target_property(_qml_uri ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_URI}) 0135 if ("${_qml_uri}" STREQUAL "" OR "${_qml_uri}" STREQUAL "${_ECM_QMLMODULE_PROPERTY_URI}-NOTFOUND") 0136 message(FATAL_ERROR "Target ${ARG_TARGET} is not a QML plugin target") 0137 endif() 0138 endmacro() 0139 0140 macro(_ecm_qmlmodule_uri_to_path ARG_OUTPUT ARG_PATH ARG_VERSION) 0141 string(REPLACE "." "/" _output "${ARG_PATH}") 0142 0143 # If the major version of the module is >2.0, Qt expects a ".MajorVersion" 0144 # suffix on the directory. So handle that. 0145 if (${ARG_VERSION} VERSION_GREATER_EQUAL 2.0) 0146 string(REGEX MATCH "^([0-9]+)" _version_major ${ARG_VERSION}) 0147 set("${ARG_OUTPUT}" "${_output}.${_version_major}") 0148 else() 0149 set("${ARG_OUTPUT}" "${_output}") 0150 endif() 0151 endmacro() 0152 0153 function(_ecm_qmlmodule_generate_qmldir ARG_TARGET) 0154 get_target_property(_qml_uri ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_URI}) 0155 get_target_property(_qml_files ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_FILES}) 0156 get_target_property(_qml_only ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_QMLONLY}) 0157 get_target_property(_qml_classname ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_CLASSNAME}) 0158 0159 set(_qmldir_template "# This file was automatically generated by ECMQmlModule and should not be modified") 0160 0161 string(APPEND _qmldir_template "\nmodule ${_qml_uri}") 0162 0163 if (NOT ${_qml_only}) 0164 string(APPEND _qmldir_template "\nplugin ${ARG_TARGET}") 0165 0166 if ("${_qml_classname}" STREQUAL "") 0167 string(APPEND _qmldir_template "\nclassname ${ARG_TARGET}") 0168 else() 0169 string(APPEND _qmldir_template "\nclassname ${_qml_classname}") 0170 endif() 0171 endif() 0172 0173 get_target_property(_qml_depends ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_DEPENDS}) 0174 if (NOT "${_qml_depends}" STREQUAL "") 0175 foreach(_depends ${_qml_depends}) 0176 string(APPEND _qmldir_template "\ndepends ${_depends}") 0177 endforeach() 0178 endif() 0179 0180 foreach(_file ${_qml_files}) 0181 get_filename_component(_filename ${_file} NAME) 0182 get_filename_component(_classname ${_file} NAME_WE) 0183 get_property(_version SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_VERSION}) 0184 get_property(_private SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PRIVATE}) 0185 if (NOT _private) 0186 string(APPEND _qmldir_template "\n${_classname} ${_version} ${_filename}") 0187 endif() 0188 endforeach() 0189 0190 string(APPEND _qmldir_template "\n") 0191 0192 file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}_qmldir" "${_qmldir_template}") 0193 set_target_properties(${ARG_TARGET} PROPERTIES _ecm_qmldir_file "${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}_qmldir") 0194 endfunction() 0195 0196 function(_ecm_qmlmodule_generate_qrc ARG_TARGET) 0197 get_target_property(_qml_uri ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_URI}) 0198 get_target_property(_qml_files ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_FILES}) 0199 get_target_property(_qmldir_file ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_QMLDIR}) 0200 get_target_property(_qml_version ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_VERSION}) 0201 0202 _ecm_qmlmodule_uri_to_path(_qml_prefix "${_qml_uri}" "${_qml_version}") 0203 0204 set(_qrc_template "<!-- This file was automatically generated by ECMQmlModule and should not be modified -->") 0205 0206 string(APPEND _qrc_template "\n<RCC>\n<qresource prefix=\"/qt-project.org/imports/${_qml_prefix}\">") 0207 string(APPEND _qrc_template "\n<file alias=\"qmldir\">${_qmldir_file}</file>") 0208 0209 foreach(_file ${_qml_files}) 0210 get_filename_component(_filename ${_file} NAME) 0211 get_property(_path SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PATH}) 0212 0213 set(_file_path "${_filename}") 0214 if (NOT "${_path}" STREQUAL "") 0215 set(_file_path "${_path}/${_filename}") 0216 endif() 0217 0218 string(APPEND _qrc_template "\n<file alias=\"${_file_path}\">${_file}</file>") 0219 endforeach() 0220 0221 string(APPEND _qrc_template "\n</qresource>\n</RCC>\n") 0222 0223 file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}.qrc" "${_qrc_template}") 0224 qt_add_resources(_qrc_output "${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}.qrc") 0225 0226 target_sources(${ARG_TARGET} PRIVATE ${_qrc_output}) 0227 endfunction() 0228 0229 function(ecm_add_qml_module ARG_TARGET) 0230 cmake_parse_arguments(PARSE_ARGV 1 ARG "NO_PLUGIN" "URI;VERSION;CLASSNAME" "") 0231 0232 if ("${ARG_TARGET}" STREQUAL "") 0233 message(FATAL_ERROR "ecm_add_qml_module called without a valid target name.") 0234 endif() 0235 0236 if ("${ARG_URI}" STREQUAL "") 0237 message(FATAL_ERROR "ecm_add_qml_module called without a valid module URI.") 0238 endif() 0239 0240 string(FIND "${ARG_URI}" " " "_space") 0241 if (${_space} GREATER_EQUAL 0) 0242 message(FATAL_ERROR "ecm_add_qml_module called without a valid module URI.") 0243 endif() 0244 0245 if (${BUILD_SHARED_LIBS} AND ${ARG_NO_PLUGIN}) 0246 # CMake doesn't like library targets without sources, so if we have no 0247 # C++ sources, use a plain target that we can add all the install stuff 0248 # to. 0249 add_custom_target(${ARG_TARGET} ALL) 0250 else() 0251 add_library(${ARG_TARGET}) 0252 endif() 0253 0254 if ("${ARG_VERSION}" STREQUAL "") 0255 set(ARG_VERSION "1.0") 0256 endif() 0257 0258 set_target_properties(${ARG_TARGET} PROPERTIES 0259 ${_ECM_QMLMODULE_PROPERTY_URI} "${ARG_URI}" 0260 ${_ECM_QMLMODULE_PROPERTY_FILES} "" 0261 ${_ECM_QMLMODULE_PROPERTY_QMLONLY} "${ARG_NO_PLUGIN}" 0262 ${_ECM_QMLMODULE_PROPERTY_VERSION} "${ARG_VERSION}" 0263 ${_ECM_QMLMODULE_PROPERTY_CLASSNAME} "${ARG_CLASSNAME}" 0264 ${_ECM_QMLMODULE_PROPERTY_DEPENDS} "" 0265 ) 0266 0267 # QQmlImportDatabase::resolvePlugin doesn't accept lib prefixes under 0268 # Windows, causing to fail to import when using as a dynamic plugin. 0269 if (MINGW) 0270 set_target_properties(${ARG_TARGET} PROPERTIES PREFIX "") 0271 endif() 0272 0273 # -Muri is required for static QML plugins to work properly, see 0274 # https://bugreports.qt.io/browse/QTBUG-82598 0275 set_target_properties(${ARG_TARGET} PROPERTIES AUTOMOC_MOC_OPTIONS -Muri=${ARG_URI}) 0276 endfunction() 0277 0278 function(ecm_add_qml_module_dependencies ARG_TARGET) 0279 cmake_parse_arguments(PARSE_ARGV 1 ARG "" "" "DEPENDS") 0280 0281 _ecm_qmlmodule_verify_qml_target(${ARG_TARGET}) 0282 0283 if ("${ARG_DEPENDS}" STREQUAL "") 0284 message(FATAL_ERROR "ecm_add_qml_module_dependencies called without required argument DEPENDS") 0285 endif() 0286 0287 set_target_properties(${ARG_TARGET} PROPERTIES ${_ECM_QMLMODULE_PROPERTY_DEPENDS} "${ARG_DEPENDS}") 0288 endfunction() 0289 0290 function(ecm_target_qml_sources ARG_TARGET) 0291 cmake_parse_arguments(PARSE_ARGV 1 ARG "PRIVATE" "VERSION;PATH" "SOURCES") 0292 0293 _ecm_qmlmodule_verify_qml_target(${ARG_TARGET}) 0294 0295 if ("${ARG_SOURCES}" STREQUAL "") 0296 message(FATAL_ERROR "ecm_target_qml_sources called without required argument SOURCES") 0297 endif() 0298 0299 if ("${ARG_VERSION}" STREQUAL "") 0300 get_target_property(ARG_VERSION ${ARG_TARGET} "_ecm_qml_version") 0301 endif() 0302 0303 foreach(_file ${ARG_SOURCES}) 0304 # Check if a given file exists, but only for files that are not 0305 # automatically generated. 0306 set(_path "${_file}") 0307 get_source_file_property(_generated ${_file} GENERATED) 0308 if (NOT _generated) 0309 if (IS_ABSOLUTE ${_path}) 0310 # Assume absolute paths are generated, which may not always be 0311 # true but is fairly likely. 0312 set(_generated TRUE) 0313 else() 0314 string(FIND ${_file} ${CMAKE_BINARY_DIR} _in_binary_dir) 0315 if (${_in_binary_dir} GREATER_EQUAL 0) 0316 # Assume anything in binary dir is generated. 0317 set(_generated TRUE) 0318 else() 0319 set(_path "${CMAKE_CURRENT_SOURCE_DIR}/${_file}") 0320 endif() 0321 endif() 0322 endif() 0323 0324 if (NOT ${_generated} AND NOT EXISTS ${_path}) 0325 message(FATAL_ERROR "ecm_target_qml_sources called with nonexistent file ${_file}") 0326 endif() 0327 0328 if (NOT ARG_PRIVATE) 0329 set_property(SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_VERSION} "${ARG_VERSION}") 0330 else() 0331 set_property(SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PRIVATE} TRUE) 0332 endif() 0333 set_property(SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PATH} "${ARG_PATH}") 0334 set_property(TARGET ${ARG_TARGET} 0335 APPEND PROPERTY ${_ECM_QMLMODULE_PROPERTY_FILES} ${_path} 0336 ) 0337 endforeach() 0338 endfunction() 0339 0340 function(ecm_finalize_qml_module ARG_TARGET) 0341 cmake_parse_arguments(PARSE_ARGV 1 ARG "" "DESTINATION" "") 0342 0343 _ecm_qmlmodule_verify_qml_target(${ARG_TARGET}) 0344 0345 if ("${ARG_DESTINATION}" STREQUAL "") 0346 message(FATAL_ERROR "ecm_finalize_qml_module called without required argument DESTINATION") 0347 endif() 0348 0349 _ecm_qmlmodule_generate_qmldir(${ARG_TARGET}) 0350 0351 get_target_property(_qml_uri ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_URI}) 0352 get_target_property(_version ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_VERSION}) 0353 0354 _ecm_qmlmodule_uri_to_path(_plugin_path "${_qml_uri}" "${_version}") 0355 0356 get_target_property(_qml_only ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_QMLONLY}) 0357 0358 if (NOT BUILD_SHARED_LIBS) 0359 _ecm_qmlmodule_generate_qrc(${ARG_TARGET}) 0360 set(CPP_RESOURCE_INIT "#include <qglobal.h> \n#include <QDebug> \n void initQmlResource${ARG_TARGET}() {Q_INIT_RESOURCE(${ARG_TARGET}); qWarning()<<Q_FUNC_INFO;};") 0361 file(WRITE ${ARGS_TARGET}_init.cpp "${CPP_RESOURCE_INIT}") 0362 target_sources(${ARG_TARGET} PRIVATE ${ARGS_TARGET}_init.cpp) 0363 target_compile_definitions(${ARG_TARGET} PRIVATE -DQT_PLUGIN_RESOURCE_INIT_FUNCTION=initQmlResource${ARG_TARGET} -DQT_STATICPLUGIN=1) 0364 0365 if (${_qml_only}) 0366 # If we do not have any C++ sources for the target, we need a way to 0367 # compile the generated QRC file. So generate a very plain C++ QML 0368 # plugin that we include in the target. 0369 configure_file(${_ECM_QMLMODULE_STATIC_QMLONLY_H} ${CMAKE_CURRENT_BINARY_DIR}/QmlModule.h @ONLY) 0370 configure_file(${_ECM_QMLMODULE_STATIC_QMLONLY_CPP} ${CMAKE_CURRENT_BINARY_DIR}/QmlModule.cpp @ONLY) 0371 0372 target_sources(${ARG_TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/QmlModule.cpp) 0373 if (TARGET Qt::Qml) 0374 target_link_libraries(${ARG_TARGET} PRIVATE Qt::Qml) 0375 else() 0376 target_link_libraries(${ARG_TARGET} PRIVATE Qt5::Qml) 0377 endif() 0378 endif() 0379 0380 install(TARGETS ${ARG_TARGET} DESTINATION ${ARG_DESTINATION}/${_plugin_path}) 0381 0382 return() 0383 endif() 0384 0385 get_target_property(_runtime_output_dir ${ARG_TARGET} RUNTIME_OUTPUT_DIRECTORY) 0386 if (NOT ${_runtime_output_dir}) 0387 if ("${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" STREQUAL "") 0388 set(_runtime_output_dir ${CMAKE_CURRENT_BINARY_DIR}) 0389 else() 0390 set(_runtime_output_dir ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 0391 endif() 0392 endif() 0393 0394 add_custom_command( 0395 TARGET ${ARG_TARGET} 0396 POST_BUILD 0397 WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 0398 COMMAND ${CMAKE_COMMAND} -E make_directory ${_runtime_output_dir}/${_plugin_path} 0399 BYPRODUCTS ${_runtime_output_dir}/${_plugin_path} 0400 ) 0401 0402 get_target_property(_qmldir_file ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_QMLDIR}) 0403 install(FILES ${_qmldir_file} DESTINATION ${ARG_DESTINATION}/${_plugin_path} RENAME "qmldir") 0404 0405 add_custom_command( 0406 TARGET ${ARG_TARGET} 0407 POST_BUILD 0408 WORKING_DIRECTORY ${_runtime_output_dir} 0409 COMMAND ${CMAKE_COMMAND} -E copy ${_qmldir_file} ${_plugin_path}/qmldir 0410 ) 0411 0412 if (NOT ${_qml_only}) 0413 install(TARGETS ${ARG_TARGET} DESTINATION ${ARG_DESTINATION}/${_plugin_path}) 0414 0415 add_custom_command( 0416 TARGET ${ARG_TARGET} 0417 POST_BUILD 0418 WORKING_DIRECTORY ${_runtime_output_dir} 0419 COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${ARG_TARGET}> ${_plugin_path} 0420 ) 0421 endif() 0422 0423 get_target_property(_qml_files ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_FILES}) 0424 foreach(_file ${_qml_files}) 0425 get_filename_component(_filename ${_file} NAME) 0426 get_property(_path SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PATH}) 0427 0428 set(_file_path "${_plugin_path}/") 0429 if (NOT "${_path}" STREQUAL "") 0430 set(_file_path "${_plugin_path}/${_path}/") 0431 endif() 0432 0433 install(FILES ${_file} DESTINATION ${ARG_DESTINATION}/${_file_path}) 0434 0435 add_custom_command( 0436 TARGET ${ARG_TARGET} 0437 POST_BUILD 0438 WORKING_DIRECTORY ${_runtime_output_dir} 0439 COMMAND ${CMAKE_COMMAND} -E make_directory ${_file_path} 0440 COMMAND ${CMAKE_COMMAND} -E copy ${_file} ${_file_path} 0441 BYPRODUCTS ${_file_path}/${_filename} 0442 ) 0443 endforeach() 0444 endfunction()