From 7a1853dbe2a3e316900ed1daf9f05deb37413d72 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 19 Aug 2019 16:19:08 +0200 Subject: Extract add_qt_resource into reusable components This patch moves all of the underlying code for add_qt_resource into a common reusable snippet for both the Qt build and user projects. For users, the new API is available under QT5_ADD_RESOURCES. If outfiles is a CMAKE target we will use the new API, otherwise we will fall back to the old behavior. This patch also adds EXTRA_CMAKE_FILES and EXTRA_CMAKE_INCLUDES to add_qt_module so that module specific cmake files can be installed and loaded by the module's config.cmake. The code will be installed under CMAKE_BINARY_DIR/Qt{}CoreResource.cmake and is injected into Qt{}Core_Config.cmake via the extra cmake includes passed into add_qt_module. To make sure it still works with QtBuild, we do the actual generation of the file from QtBaseGlobalTargets and include the generated file there as well. Change-Id: I85fefaa11dde01a6790d23c62d6a64cd157e2617 Reviewed-by: Alexandru Croitor --- cmake/QtResource.cmake.in | 270 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 cmake/QtResource.cmake.in (limited to 'cmake/QtResource.cmake.in') diff --git a/cmake/QtResource.cmake.in b/cmake/QtResource.cmake.in new file mode 100644 index 0000000000..cc396fc319 --- /dev/null +++ b/cmake/QtResource.cmake.in @@ -0,0 +1,270 @@ +# +# All things resource related +# + +function(__qt_get_relative_resource_path_for_file output_alias file) + get_property(alias SOURCE ${file} PROPERTY QT_RESOURCE_ALIAS) + if (NOT alias) + set(alias "${file}") + endif() + set(${output_alias} ${alias} PARENT_SCOPE) +endfunction() + +function(__qt_propagate_generated_resource target resource_name generated_source_code output_generated_target) + get_target_property(type ${target} TYPE) + if(type STREQUAL STATIC_LIBRARY) + set(resource_target "${target}_resources_${resourceName}") + add_library("${resource_target}" OBJECT "${generated_source_code}") + target_link_libraries(${target} INTERFACE "$") + set(${output_generated_target} "${resource_target}" PARENT_SCOPE) + else() + set(${output_generated_target} "" PARENT_SCOPE) + target_sources(${target} PRIVATE ${generated_source_code}) + endif() +endfunction() + +# Inspect all files passed to a call to add_qt_resource. If there are any +# files present, invoke the quick compiler and return the remaining resource +# files that have not been prossed in REMAING_RESOURCES as well as the new +# name for the resource in OUTPUT_RESOURCE_NAME. +function(__qt_quick_compiler_process_resources target resource_name) + + cmake_parse_arguments(arg + "" "PREFIX;OUTPUT_REMAINING_RESOURCES;OUTPUT_RESOURCE_NAME;OUTPUT_GENERATED_TARGET" "FILES" ${ARGN} + ) + + set(qml_files) + set(resource_files) + set(retained_files) + # scan for qml files + foreach(file IN LISTS arg_FILES) + # check whether this resource should not be processed by the qt quick + # compiler + get_source_file_property(skip_compiler_check ${file} QT_SKIP_QUICKCOMPILER) + get_source_file_property(retain_compiler_check ${file} QT_RETAIN_QUICKCOMPILER) + if (skip_compiler_check) + list(APPEND resource_files ${file}) + continue() + endif() + + if (${file} MATCHES "\.js$" + OR ${file} MATCHES "\.mjs$" + OR ${file} MATCHES "\.qml$") + list(APPEND qml_files ${file}) + if (retain_compiler_check) + list(APPEND retained_files ${file}) + list(APPEND resource_files ${file}) + endif() + else() + list(APPEND resource_files ${file}) + endif() + endforeach() + if (NOT TARGET @QT_CMAKE_EXPORT_NAMESPACE@::qmlcachegen AND qml_files) + message(WARNING "QT@PROJECT_VERSION_MAJOR@_PROCESS_RESOURCE: Qml files were detected but the qmlcachgen target is not defined. Consider adding QmlTools to your find_package command.") + endif() + + set(retained_resource_paths) + if (TARGET @QT_CMAKE_EXPORT_NAMESPACE@::qmlcachegen AND qml_files) + # Enable qt quick compiler support + set(qml_resource_file "${CMAKE_CURRENT_BINARY_DIR}/${resource_name}.qrc") + if (resource_files) + set(chained_resource_name "${resource_name}_qmlcache") + endif() + + foreach(file IN LISTS qml_files) + get_filename_component(file_absolute ${file} ABSOLUTE) + file(RELATIVE_PATH file_relative ${CMAKE_CURRENT_SOURCE_DIR} ${file_absolute}) + __qt_get_relative_resource_path_for_file(file_resource_path ${file}) + if (arg_PREFIX STREQUAL "/") + # TO_CMAKE_PATH does not clean up cases such as //Foo + set(file_resource_path "/${file_resource_path}") + else() + set(file_resource_path "${arg_PREFIX}/${file_resource_path}") + endif() + if (file IN_LIST retained_files) + list(APPEND retained_resource_paths ${file_resource_path}) + endif() + file(TO_CMAKE_PATH ${file_resource_path} file_resource_path) + list(APPEND file_resource_paths ${file_resource_path}) + string(REGEX REPLACE "\.js$" "_js" compiled_file ${file_relative}) + string(REGEX REPLACE "\.mjs$" "_mjs" compiled_file ${compiled_file}) + string(REGEX REPLACE "\.qml$" "_qml" compiled_file ${compiled_file}) + set(compiled_file "${CMAKE_CURRENT_BINARY_DIR}/qmlcache/${resource_name}/${compiled_file}.cpp") + add_custom_command( + OUTPUT ${compiled_file} + DEPENDS ${file_absolute} + COMMAND + @QT_CMAKE_EXPORT_NAMESPACE@::qmlcachegen + --resource-path ${file_resource_path} + -o ${compiled_file} + ${file_absolute} + ) + target_sources(${target} PRIVATE ${compiled_file}) + endforeach() + + set(qmlcache_loader_list "${CMAKE_CURRENT_BINARY_DIR}/qmlcache/${resource_name}/qml_loader_file_list.rsp") + file(GENERATE + OUTPUT ${qmlcache_loader_list} + CONTENT "$" + ) + + set(qmlcache_loader_file "${CMAKE_CURRENT_BINARY_DIR}/qmlcache/${resource_name}/qmlcache_loader.cpp") + set(resource_name_arg "${resource_name}.qrc") + if (chained_resource_name) + set(resource_name_arg "${resource_name_arg}=${chained_resource_name}") + endif() + + if (retained_resource_paths) + set(retained_loader_list "${CMAKE_CURRENT_BINARY_DIR}/qmlcache/${resource_name}/retained_file_list.rsp") + file(GENERATE + OUTPUT ${retained_loader_list} + CONTENT "$" + ) + set(retained_args "--retain" "@${retained_loader_list}") + endif() + + add_custom_command( + OUTPUT ${qmlcache_loader_file} + DEPENDS ${qmlcache_loader_list} + COMMAND + @QT_CMAKE_EXPORT_NAMESPACE@::qmlcachegen + ${retained_args} + --resource-name "${resource_name_arg}" + -o ${qmlcache_loader_file} + "@${qmlcache_loader_list}" + ) + + __qt_propagate_generated_resource(${target} + ${resource_name} + ${qmlcache_loader_file} + output_target) + + set(${arg_OUTPUT_GENERATED_TARGET} "${output_target}" PARENT_SCOPE) + + if (resource_files) + set(resource_name ${chained_resource_name}) + endif() + else() + set(resource_files ${arg_FILES}) + endif() + + set(${arg_OUTPUT_REMAINING_RESOURCES} ${resource_files} PARENT_SCOPE) + set(${arg_OUTPUT_RESOURCE_NAME} ${resource_name} PARENT_SCOPE) + +endfunction() + +# +# Process resources via file path instead of QRC files. Behind the +# scnenes, it will generate a qrc file and apply post processing steps +# when applicable. (e.g.: QtQuickCompiler) +# +# The QRC Prefix is set via the PREFIX parameter. +# +# Alias settings for files need to be set via the QT_RESOURCE_ALIAS property +# via the set_soure_files_properties() command. +# +# When using this command with static libraries, a special target will be +# generated. Should you wish to perform additional processing on said target +# pass a value to the OUTPUT_TARGET parameter. +# +function(QT@PROJECT_VERSION_MAJOR@_PROCESS_RESOURCE target resourceName) + + cmake_parse_arguments(rcc "" "PREFIX;LANG;BASE;OUTPUT_TARGET" "FILES" ${ARGN}) + + string(REPLACE "/" "_" resourceName ${resourceName}) + string(REPLACE "." "_" resourceName ${resourceName}) + + # Apply base to all files + if (rcc_BASE) + foreach(file IN LISTS rcc_FILES) + set(resource_file "${rcc_BASE}/${file}") + __qt_get_relative_resource_path_for_file(alias ${resource_file}) + # Handle case where resources were generated from a directory + # different than the one where the main .pro file resides. + # Unless otherwise specified, we should use the original file path + # as alias. + if (alias STREQUAL resource_file) + set_source_files_properties(${resource_file} PROPERTIES QT_RESOURCE_ALIAS ${file}) + endif() + file(TO_CMAKE_PATH ${resource_file} resource_file) + list(APPEND resource_files ${resource_file}) + endforeach() + else() + set(resource_files ${rcc_FILES}) + endif() + + if(NOT rcc_PREFIX) + get_target_property(rcc_PREFIX ${target} QT_RESOURCE_PREFIX) + if (NOT rcc_PREFIX) + message(FATAL_ERROR "QT@PROJECT_VERSION_MAJOR@_PROCESS_RESOURCE() was called without a PREFIX and the target does not provide QT_RESOURCE_PREFIX. Please either add a PREFIX or make the target ${target} provide a default.") + endif() + endif() + + # Apply quick compiler pass + __qt_quick_compiler_process_resources(${target} ${resourceName} + FILES ${resource_files} + PREFIX ${rcc_PREFIX} + OUTPUT_REMAINING_RESOURCES resources + OUTPUT_RESOURCE_NAME newResourceName + OUTPUT_GENERATED_TARGET output_target + ) + + if (NOT resources) + return() + endif() + set(generatedResourceFile "${CMAKE_CURRENT_BINARY_DIR}/generated_${newResourceName}.qrc") + set(generatedSourceCode "${CMAKE_CURRENT_BINARY_DIR}/qrc_${newResourceName}.cpp") + + # Generate .qrc file: + + # + set(qrcContents "\n \n") + + foreach(file IN LISTS resources) + __qt_get_relative_resource_path_for_file(file_resource_path ${file}) + + if (NOT IS_ABSOLUTE ${file}) + set(file "${CMAKE_CURRENT_SOURCE_DIR}/${file}") + endif() + + ### FIXME: escape file paths to be XML conform + # ... + string(APPEND qrcContents " ") + string(APPEND qrcContents "${file}\n") + list(APPEND files "${file}") + endforeach() + + # + string(APPEND qrcContents " \n\n") + + file(GENERATE OUTPUT "${generatedResourceFile}" CONTENT "${qrcContents}") + + # Process .qrc file: + add_custom_command(OUTPUT "${generatedSourceCode}" + COMMAND "@QT_CMAKE_EXPORT_NAMESPACE@::rcc" + ARGS --name "${newResourceName}" + --output "${generatedSourceCode}" "${generatedResourceFile}" + DEPENDS ${resources} ${generatedResourceFile} + COMMENT "RCC ${newResourceName}" + VERBATIM) + + get_target_property(type "${target}" TYPE) + # Only do this if newResourceName is the same as resourceName, since + # the resource will be chainloaded by the qt quickcompiler + # qml cache loader + if(newResourceName STREQUAL resourceName) + __qt_propagate_generated_resource(${target} ${resourceName} "${generatedSourceCode}" output_target) + else() + target_sources(${target} PRIVATE "${generatedSourceCode}") + endif() + if (arg_OUTPUT_TARGET) + set(${arg_OUPUT_TARGET} "${output_target}" PARENT_SCOPE) + endif() +endfunction() -- cgit v1.2.3