# Copyright 2005-2011 Kitware, Inc. # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause ###################################### # # Macros for building Qt files # ###################################### set(__qt_core_macros_module_base_dir "${CMAKE_CURRENT_LIST_DIR}") # macro used to create the names of output files preserving relative dirs macro(_qt_internal_make_output_file infile prefix ext outfile ) string(LENGTH ${CMAKE_CURRENT_BINARY_DIR} _binlength) string(LENGTH ${infile} _infileLength) set(_checkinfile ${CMAKE_CURRENT_SOURCE_DIR}) if(_infileLength GREATER _binlength) string(SUBSTRING "${infile}" 0 ${_binlength} _checkinfile) if(_checkinfile STREQUAL "${CMAKE_CURRENT_BINARY_DIR}") file(RELATIVE_PATH rel ${CMAKE_CURRENT_BINARY_DIR} ${infile}) else() file(RELATIVE_PATH rel ${CMAKE_CURRENT_SOURCE_DIR} ${infile}) endif() else() file(RELATIVE_PATH rel ${CMAKE_CURRENT_SOURCE_DIR} ${infile}) endif() if(WIN32 AND rel MATCHES "^([a-zA-Z]):(.*)$") # absolute path set(rel "${CMAKE_MATCH_1}_${CMAKE_MATCH_2}") endif() set(_outfile "${CMAKE_CURRENT_BINARY_DIR}/${rel}") string(REPLACE ".." "__" _outfile ${_outfile}) get_filename_component(outpath ${_outfile} PATH) if(CMAKE_VERSION VERSION_LESS "3.14") get_filename_component(_outfile_ext ${_outfile} EXT) get_filename_component(_outfile_ext ${_outfile_ext} NAME_WE) get_filename_component(_outfile ${_outfile} NAME_WE) string(APPEND _outfile ${_outfile_ext}) else() get_filename_component(_outfile ${_outfile} NAME_WLE) endif() file(MAKE_DIRECTORY ${outpath}) set(${outfile} ${outpath}/${prefix}${_outfile}.${ext}) endmacro() macro(_qt_internal_get_moc_flags _moc_flags) set(${_moc_flags}) get_directory_property(_inc_DIRS INCLUDE_DIRECTORIES) if(CMAKE_INCLUDE_CURRENT_DIR) list(APPEND _inc_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) endif() foreach(_current ${_inc_DIRS}) if("${_current}" MATCHES "\\.framework/?$") string(REGEX REPLACE "/[^/]+\\.framework" "" framework_path "${_current}") set(${_moc_flags} ${${_moc_flags}} "-F${framework_path}") else() set(${_moc_flags} ${${_moc_flags}} "-I${_current}") endif() endforeach() get_directory_property(_defines COMPILE_DEFINITIONS) foreach(_current ${_defines}) set(${_moc_flags} ${${_moc_flags}} "-D${_current}") endforeach() if(WIN32) set(${_moc_flags} ${${_moc_flags}} -DWIN32) endif() if (MSVC) set(${_moc_flags} ${${_moc_flags}} --compiler-flavor=msvc) endif() endmacro() # helper macro to set up a moc rule function(_qt_internal_create_moc_command infile outfile moc_flags moc_options moc_target moc_depends out_json_file) # Pass the parameters in a file. Set the working directory to # be that containing the parameters file and reference it by # just the file name. This is necessary because the moc tool on # MinGW builds does not seem to handle spaces in the path to the # file given with the @ syntax. get_filename_component(_moc_outfile_name "${outfile}" NAME) get_filename_component(_moc_outfile_dir "${outfile}" PATH) if(_moc_outfile_dir) set(_moc_working_dir WORKING_DIRECTORY ${_moc_outfile_dir}) endif() set (_moc_parameters_file ${outfile}_parameters) set (_moc_parameters ${moc_flags} ${moc_options} -o "${outfile}" "${infile}") if(out_json_file) list(APPEND _moc_parameters --output-json) set(extra_output_files "${outfile}.json") set(${out_json_file} "${extra_output_files}" PARENT_SCOPE) endif() if(moc_target) set(_moc_parameters_file ${_moc_parameters_file}$<$>:_$>) set(targetincludes "$") set(targetdefines "$") set(targetincludes "$<$:-I$>") set(targetdefines "$<$:-D$>") string(REPLACE ">" "$" _moc_escaped_parameters "${_moc_parameters}") string(REPLACE "," "$" _moc_escaped_parameters "${_moc_escaped_parameters}") set(concatenated "$<$:${targetincludes};>$<$:${targetdefines};>$<$:${_moc_escaped_parameters};>") set(concatenated "$,EXCLUDE,^-[DI]$>") set(concatenated "$") file (GENERATE OUTPUT ${_moc_parameters_file} CONTENT "${concatenated}" ) set(concatenated) set(targetincludes) set(targetdefines) else() string (REPLACE ";" "\n" _moc_parameters "${_moc_parameters}") file(WRITE ${_moc_parameters_file} "${_moc_parameters}\n") endif() set(_moc_extra_parameters_file @${_moc_parameters_file}) add_custom_command(OUTPUT ${outfile} ${extra_output_files} COMMAND ${QT_CMAKE_EXPORT_NAMESPACE}::moc ${_moc_extra_parameters_file} DEPENDS ${infile} ${moc_depends} ${_moc_working_dir} VERBATIM) set_source_files_properties(${infile} PROPERTIES SKIP_AUTOMOC ON) set_source_files_properties(${outfile} PROPERTIES SKIP_AUTOMOC ON SKIP_AUTOUIC ON ) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.27") set_source_files_properties(${outfile} PROPERTIES SKIP_LINTING ON) endif() endfunction() function(qt6_generate_moc infile outfile ) # get include dirs and flags _qt_internal_get_moc_flags(moc_flags) get_filename_component(abs_infile ${infile} ABSOLUTE) set(_outfile "${outfile}") if(NOT IS_ABSOLUTE "${outfile}") set(_outfile "${CMAKE_CURRENT_BINARY_DIR}/${outfile}") endif() if ("x${ARGV2}" STREQUAL "xTARGET") set(moc_target ${ARGV3}) endif() _qt_internal_create_moc_command(${abs_infile} ${_outfile} "${moc_flags}" "" "${moc_target}" "" # moc_depends "" # out_json_file ) endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) function(qt_generate_moc) if(QT_DEFAULT_MAJOR_VERSION EQUAL 5) qt5_generate_moc(${ARGV}) elseif(QT_DEFAULT_MAJOR_VERSION EQUAL 6) qt6_generate_moc(${ARGV}) endif() endfunction() endif() # qt6_wrap_cpp(outfiles inputfile ... ) function(qt6_wrap_cpp outfiles ) # get include dirs _qt_internal_get_moc_flags(moc_flags) set(options) set(oneValueArgs TARGET __QT_INTERNAL_OUTPUT_MOC_JSON_FILES ) set(multiValueArgs OPTIONS DEPENDS) cmake_parse_arguments(_WRAP_CPP "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(moc_files ${_WRAP_CPP_UNPARSED_ARGUMENTS}) set(moc_options ${_WRAP_CPP_OPTIONS}) set(moc_target ${_WRAP_CPP_TARGET}) set(moc_depends ${_WRAP_CPP_DEPENDS}) set(metatypes_json_list "") foreach(it ${moc_files}) get_filename_component(it ${it} ABSOLUTE) _qt_internal_make_output_file(${it} moc_ cpp outfile) set(out_json_file_var "") if(_WRAP_CPP___QT_INTERNAL_OUTPUT_MOC_JSON_FILES) set(out_json_file_var "out_json_file") endif() _qt_internal_create_moc_command( ${it} ${outfile} "${moc_flags}" "${moc_options}" "${moc_target}" "${moc_depends}" "${out_json_file_var}") list(APPEND ${outfiles} ${outfile}) if(_WRAP_CPP___QT_INTERNAL_OUTPUT_MOC_JSON_FILES) list(APPEND metatypes_json_list "${${out_json_file_var}}") endif() endforeach() set(${outfiles} ${${outfiles}} PARENT_SCOPE) if(metatypes_json_list) set(${_WRAP_CPP___QT_INTERNAL_OUTPUT_MOC_JSON_FILES} "${metatypes_json_list}" PARENT_SCOPE) endif() endfunction() # This will override the CMake upstream command, because that one is for Qt 3. if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) function(qt_wrap_cpp outfiles) if(QT_DEFAULT_MAJOR_VERSION EQUAL 5) qt5_wrap_cpp("${outfiles}" ${ARGN}) elseif(QT_DEFAULT_MAJOR_VERSION EQUAL 6) qt6_wrap_cpp("${outfiles}" ${ARGN}) endif() set("${outfiles}" "${${outfiles}}" PARENT_SCOPE) endfunction() endif() # _qt6_parse_qrc_file(infile _out_depends _rc_depends) # internal function(_qt6_parse_qrc_file infile _out_depends _rc_depends) get_filename_component(rc_path ${infile} PATH) if(EXISTS "${infile}") # parse file for dependencies # all files are absolute paths or relative to the location of the qrc file file(READ "${infile}" RC_FILE_CONTENTS) string(REGEX MATCHALL "]*>" "" RC_FILE "${RC_FILE}") if(NOT IS_ABSOLUTE "${RC_FILE}") set(RC_FILE "${rc_path}/${RC_FILE}") endif() set(RC_DEPENDS ${RC_DEPENDS} "${RC_FILE}") endforeach() # Since this cmake macro is doing the dependency scanning for these files, # let's make a configured file and add it as a dependency so cmake is run # again when dependencies need to be recomputed. _qt_internal_make_output_file("${infile}" "" "qrc.depends" out_depends) configure_file("${infile}" "${out_depends}" COPYONLY) else() # The .qrc file does not exist (yet). Let's add a dependency and hope # that it will be generated later set(out_depends) endif() set(${_out_depends} ${out_depends} PARENT_SCOPE) set(${_rc_depends} ${RC_DEPENDS} PARENT_SCOPE) endfunction() # qt6_add_binary_resources(target inputfiles ... ) function(qt6_add_binary_resources target ) set(options) set(oneValueArgs DESTINATION) set(multiValueArgs OPTIONS) cmake_parse_arguments(_RCC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(rcc_files ${_RCC_UNPARSED_ARGUMENTS}) set(rcc_options ${_RCC_OPTIONS}) set(rcc_destination ${_RCC_DESTINATION}) if(NOT QT_FEATURE_zstd) list(APPEND rcc_options "--no-zstd") endif() if(NOT rcc_destination) set(rcc_destination ${CMAKE_CURRENT_BINARY_DIR}/${target}.rcc) endif() foreach(it ${rcc_files}) get_filename_component(infile ${it} ABSOLUTE) _qt6_parse_qrc_file(${infile} _out_depends _rc_depends) set_source_files_properties(${infile} PROPERTIES SKIP_AUTORCC ON) set(infiles ${infiles} ${infile}) set(out_depends ${out_depends} ${_out_depends}) set(rc_depends ${rc_depends} ${_rc_depends}) endforeach() add_custom_command(OUTPUT ${rcc_destination} DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::rcc COMMAND ${QT_CMAKE_EXPORT_NAMESPACE}::rcc ARGS ${rcc_options} --binary --name ${target} --output ${rcc_destination} ${infiles} DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::rcc ${rc_depends} ${out_depends} ${infiles} VERBATIM) add_custom_target(${target} ALL DEPENDS ${rcc_destination}) endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) function(qt_add_binary_resources) if(QT_DEFAULT_MAJOR_VERSION EQUAL 5) qt5_add_binary_resources(${ARGV}) elseif(QT_DEFAULT_MAJOR_VERSION EQUAL 6) qt6_add_binary_resources(${ARGV}) endif() endfunction() endif() # qt6_add_resources(target resourcename ... # or # qt6_add_resources(outfiles inputfile ... ) function(qt6_add_resources outfiles ) if (TARGET ${outfiles}) cmake_parse_arguments(arg "" "OUTPUT_TARGETS" "" ${ARGN}) _qt_internal_process_resource(${ARGV}) if (arg_OUTPUT_TARGETS) set(${arg_OUTPUT_TARGETS} ${${arg_OUTPUT_TARGETS}} PARENT_SCOPE) endif() else() set(options) set(oneValueArgs) set(multiValueArgs OPTIONS) cmake_parse_arguments(_RCC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(rcc_files ${_RCC_UNPARSED_ARGUMENTS}) set(rcc_options ${_RCC_OPTIONS}) if("${rcc_options}" MATCHES "-binary") message(WARNING "Use qt6_add_binary_resources for binary option") endif() if(NOT QT_FEATURE_zstd) list(APPEND rcc_options "--no-zstd") endif() foreach(it ${rcc_files}) get_filename_component(outfilename ${it} NAME_WE) get_filename_component(infile ${it} ABSOLUTE) set(outfile ${CMAKE_CURRENT_BINARY_DIR}/qrc_${outfilename}.cpp) _qt6_parse_qrc_file(${infile} _out_depends _rc_depends) set_source_files_properties(${infile} PROPERTIES SKIP_AUTORCC ON) add_custom_command(OUTPUT ${outfile} COMMAND ${QT_CMAKE_EXPORT_NAMESPACE}::rcc ARGS ${rcc_options} --name ${outfilename} --output ${outfile} ${infile} MAIN_DEPENDENCY ${infile} DEPENDS ${_rc_depends} "${_out_depends}" ${QT_CMAKE_EXPORT_NAMESPACE}::rcc VERBATIM) set_source_files_properties(${outfile} PROPERTIES SKIP_AUTOMOC ON SKIP_AUTOUIC ON SKIP_UNITY_BUILD_INCLUSION ON SKIP_PRECOMPILE_HEADERS ON ) list(APPEND ${outfiles} ${outfile}) endforeach() set(${outfiles} ${${outfiles}} PARENT_SCOPE) endif() endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) function(qt_add_resources outfiles) if(QT_DEFAULT_MAJOR_VERSION EQUAL 5) qt5_add_resources("${outfiles}" ${ARGN}) elseif(QT_DEFAULT_MAJOR_VERSION EQUAL 6) qt6_add_resources("${outfiles}" ${ARGN}) endif() if(TARGET ${outfiles}) cmake_parse_arguments(PARSE_ARGV 1 arg "" "OUTPUT_TARGETS" "") if (arg_OUTPUT_TARGETS) set(${arg_OUTPUT_TARGETS} ${${arg_OUTPUT_TARGETS}} PARENT_SCOPE) endif() else() set("${outfiles}" "${${outfiles}}" PARENT_SCOPE) endif() endfunction() endif() # qt6_add_big_resources(outfiles inputfile ... ) function(qt6_add_big_resources outfiles ) if(CMAKE_GENERATOR STREQUAL "Xcode" AND IOS) message(WARNING "Due to CMake limitations, qt6_add_big_resources can't be used when building for iOS. " "See https://bugreports.qt.io/browse/QTBUG-103497 for details. " "Falling back to using qt6_add_resources. " "Consider using qt6_add_resources directly to silence this warning." ) qt6_add_resources(${ARGV}) set(${outfiles} ${${outfiles}} PARENT_SCOPE) return() endif() if (CMAKE_VERSION VERSION_LESS 3.9) message(FATAL_ERROR, "qt6_add_big_resources requires CMake 3.9 or newer") endif() set(options) set(oneValueArgs) set(multiValueArgs OPTIONS) cmake_parse_arguments(_RCC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(rcc_files ${_RCC_UNPARSED_ARGUMENTS}) set(rcc_options ${_RCC_OPTIONS}) if("${rcc_options}" MATCHES "-binary") message(WARNING "Use qt6_add_binary_resources for binary option") endif() if(NOT QT_FEATURE_zstd) list(APPEND rcc_options "--no-zstd") endif() foreach(it ${rcc_files}) get_filename_component(outfilename ${it} NAME_WE) # Provide unique targets and output file names # in case we add multiple .qrc files with the same base name. string(MAKE_C_IDENTIFIER "_qt_big_resource_count_${outfilename}" prop) get_property(count GLOBAL PROPERTY ${prop}) if(count) string(APPEND outfilename "_${count}") else() set(count 0) endif() math(EXPR count "${count} + 1") set_property(GLOBAL PROPERTY ${prop} ${count}) get_filename_component(infile ${it} ABSOLUTE) set(tmpoutfile ${CMAKE_CURRENT_BINARY_DIR}/qrc_${outfilename}tmp.cpp) set(outfile ${CMAKE_CURRENT_BINARY_DIR}/qrc_${outfilename}.o) _qt6_parse_qrc_file(${infile} _out_depends _rc_depends) set_source_files_properties(${infile} PROPERTIES SKIP_AUTOGEN ON) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.27") set_source_files_properties(${tmpoutfile} PROPERTIES SKIP_LINTING ON) endif() add_custom_command(OUTPUT ${tmpoutfile} COMMAND ${QT_CMAKE_EXPORT_NAMESPACE}::rcc ${rcc_options} --name ${outfilename} --pass 1 --output ${tmpoutfile} ${infile} DEPENDS ${infile} ${_rc_depends} "${out_depends}" ${QT_CMAKE_EXPORT_NAMESPACE}::rcc COMMENT "Running rcc pass 1 for resource ${outfilename}" VERBATIM) add_custom_target(big_resources_${outfilename} ALL DEPENDS ${tmpoutfile}) _qt_internal_add_rcc_pass2( RESOURCE_NAME ${outfilename} RCC_OPTIONS ${rcc_options} OBJECT_LIB rcc_object_${outfilename} QRC_FILE ${infile} PASS1_OUTPUT_FILE ${tmpoutfile} OUT_OBJECT_FILE ${outfile} ) add_dependencies(rcc_object_${outfilename} big_resources_${outfilename}) list(APPEND ${outfiles} ${outfile}) endforeach() set(${outfiles} ${${outfiles}} PARENT_SCOPE) endfunction() function(_qt_internal_add_rcc_pass2) set(options) set(oneValueArgs RESOURCE_NAME OBJECT_LIB QRC_FILE PASS1_OUTPUT_FILE OUT_OBJECT_FILE ) set(multiValueArgs RCC_OPTIONS ) cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) add_library(${arg_OBJECT_LIB} OBJECT ${arg_PASS1_OUTPUT_FILE}) _qt_internal_set_up_static_runtime_library(${arg_OBJECT_LIB}) target_compile_definitions(${arg_OBJECT_LIB} PUBLIC "$") set_target_properties(${arg_OBJECT_LIB} PROPERTIES AUTOMOC OFF AUTOUIC OFF) # The modification of TARGET_OBJECTS needs the following change in cmake # https://gitlab.kitware.com/cmake/cmake/commit/93c89bc75ceee599ba7c08b8fe1ac5104942054f add_custom_command( OUTPUT ${arg_OUT_OBJECT_FILE} COMMAND ${QT_CMAKE_EXPORT_NAMESPACE}::rcc ${arg_RCC_OPTIONS} --name ${arg_RESOURCE_NAME} --pass 2 --temp $ --output ${arg_OUT_OBJECT_FILE} ${arg_QRC_FILE} DEPENDS ${arg_OBJECT_LIB} $ ${QT_CMAKE_EXPORT_NAMESPACE}::rcc COMMENT "Running rcc pass 2 for resource ${arg_RESOURCE_NAME}" VERBATIM) endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) function(qt_add_big_resources outfiles) if(QT_DEFAULT_MAJOR_VERSION EQUAL 5) qt5_add_big_resources(${outfiles} ${ARGN}) elseif(QT_DEFAULT_MAJOR_VERSION EQUAL 6) qt6_add_big_resources(${outfiles} ${ARGN}) endif() set("${outfiles}" "${${outfiles}}" PARENT_SCOPE) endfunction() endif() function(_qt_internal_apply_win_prefix_and_suffix target) if(WIN32) # Table of prefix / suffixes for MSVC libraries as qmake expects them to be created. # static - Qt6EdidSupport.lib (platform support libraries / or static QtCore, etc) # shared - Qt6Core.dll # shared import library - Qt6Core.lib # module aka Qt plugin - qwindows.dll # module import library - qwindows.lib # # The CMake defaults are fine for us. # Table of prefix / suffixes for MinGW libraries as qmake expects them to be created. # static - libQt6EdidSupport.a (platform support libraries / or static QtCore, etc) # shared - Qt6Core.dll # shared import library - libQt6Core.a # module aka Qt plugin - qwindows.dll # module import library - libqwindows.a # # CMake for Windows-GNU platforms defaults the prefix to "lib". # CMake for Windows-GNU platforms defaults the import suffix to ".dll.a". # These CMake defaults are not ok for us. # This should cover both MINGW with GCC and CLANG. if(NOT MSVC) set_property(TARGET "${target}" PROPERTY IMPORT_SUFFIX ".a") get_target_property(target_type ${target} TYPE) if(target_type STREQUAL "STATIC_LIBRARY") set_property(TARGET "${target}" PROPERTY PREFIX "lib") else() set_property(TARGET "${target}" PROPERTY PREFIX "") set_property(TARGET "${target}" PROPERTY IMPORT_PREFIX "lib") endif() endif() endif() endfunction() set(_Qt6_COMPONENT_PATH "${CMAKE_CURRENT_LIST_DIR}/..") # Wrapper function that adds an executable with some Qt specific behavior. # Some scenarios require steps to be deferred to the end of the current # directory scope so that the caller has an opportunity to modify certain # target properties. function(qt6_add_executable target) cmake_parse_arguments(PARSE_ARGV 1 arg "MANUAL_FINALIZATION" "" "") _qt_internal_create_executable("${target}" ${arg_UNPARSED_ARGUMENTS}) target_link_libraries("${target}" PRIVATE Qt6::Core) set_property(TARGET ${target} PROPERTY _qt_expects_finalization TRUE) if(arg_MANUAL_FINALIZATION) # Caller says they will call qt6_finalize_target() themselves later return() endif() # Defer the finalization if we can. When the caller's project requires # CMake 3.19 or later, this makes the calls to this function concise while # still allowing target property modification before finalization. if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19) # Need to wrap in an EVAL CODE or else ${target} won't be evaluated # due to special behavior of cmake_language() argument handling cmake_language(EVAL CODE "cmake_language(DEFER CALL qt6_finalize_target ${target})") else() set_target_properties("${target}" PROPERTIES _qt_is_immediately_finalized TRUE) qt6_finalize_target("${target}") endif() endfunction() function(_qt_internal_create_executable target) if(ANDROID) list(REMOVE_ITEM ARGN "WIN32" "MACOSX_BUNDLE") add_library("${target}" MODULE ${ARGN}) # On our qmake builds we do don't compile the executables with # visibility=hidden. Not having this flag set will cause the # executable to have main() hidden and can then no longer be loaded # through dlopen() set_property(TARGET "${target}" PROPERTY C_VISIBILITY_PRESET default) set_property(TARGET "${target}" PROPERTY CXX_VISIBILITY_PRESET default) set_property(TARGET "${target}" PROPERTY OBJC_VISIBILITY_PRESET default) set_property(TARGET "${target}" PROPERTY OBJCXX_VISIBILITY_PRESET default) set_property(TARGET "${target}" PROPERTY _qt_android_apply_arch_suffix_called_from_qt_impl TRUE) qt6_android_apply_arch_suffix("${target}") set_property(TARGET "${target}" PROPERTY _qt_is_android_executable TRUE) else() add_executable("${target}" ${ARGN}) endif() _qt_internal_set_up_static_runtime_library("${target}") endfunction() function(_qt_internal_finalize_executable target) # We can't evaluate generator expressions at configure time, so we can't # ask for any transitive properties or even the full library dependency # chain. # We can still look at the immediate dependencies # (and recursively their dependencies) and query # any that are not expressed as generator expressions. For any we can # identify as a CMake target known to the current scope, we can check if # that target has a finalizer to be called. This is expected to cover the # vast majority of use cases, since projects will typically link directly # to Qt::* targets. For the cases where this isn't so, the project will be # responsible for calling any relevant functions themselves instead of # relying on these automatic finalization calls. set(finalizers) __qt_internal_collect_all_target_dependencies("${target}" dep_targets) if(dep_targets) foreach(dep IN LISTS dep_targets) get_target_property(dep_finalizers ${dep} INTERFACE_QT_EXECUTABLE_FINALIZERS ) if(dep_finalizers) list(APPEND finalizers ${dep_finalizers}) endif() endforeach() list(REMOVE_DUPLICATES finalizers) endif() if(finalizers) if(CMAKE_VERSION VERSION_LESS 3.18) # cmake_language() not available, fall back to the slower method of # writing a file and including it set(contents "") foreach(finalizer_func IN LISTS finalizers) string(APPEND contents "${finalizer_func}(${target})\n") endforeach() set(finalizer_file "${CMAKE_CURRENT_BINARY_DIR}/.qt/finalize_${target}.cmake") file(WRITE ${finalizer_file} "${contents}") include(${finalizer_file}) else() foreach(finalizer_func IN LISTS finalizers) cmake_language(CALL ${finalizer_func} ${target}) endforeach() endif() endif() if(EMSCRIPTEN) _qt_internal_wasm_add_target_helpers("${target}") _qt_internal_add_wasm_extra_exported_methods("${target}") endif() if(IOS) _qt_internal_finalize_ios_app("${target}") elseif(APPLE) _qt_internal_finalize_macos_app("${target}") endif() # For finalizer mode of plugin importing to work safely, we need to know the list of Qt # dependencies the target has, but those might be added later than the qt_add_executable call. # Most of our examples are like that. Only enable finalizer mode when we are sure that the user # manually called qt_finalize_target at the end of their CMake project, or it was automatically # done via a deferred call. This is also applicable to the resource object finalizer. get_target_property(is_immediately_finalized "${target}" _qt_is_immediately_finalized) if(NOT is_immediately_finalized) __qt_internal_apply_plugin_imports_finalizer_mode("${target}") __qt_internal_process_dependency_object_libraries("${target}") endif() endfunction() function(_cat IN_FILE OUT_FILE) file(READ ${IN_FILE} CONTENTS) file(APPEND ${OUT_FILE} "${CONTENTS}\n") endfunction() function(_qt_internal_finalize_batch name) find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS Core) set(generated_blacklist_file "${CMAKE_CURRENT_BINARY_DIR}/BLACKLIST") get_target_property(blacklist_files "${name}" _qt_blacklist_files) file(WRITE "${generated_blacklist_file}" "") foreach(blacklist_file ${blacklist_files}) _cat("${blacklist_file}" "${generated_blacklist_file}") endforeach() qt_internal_add_resource(${name} "batch_blacklist" PREFIX "/" FILES "${CMAKE_CURRENT_BINARY_DIR}/BLACKLIST" BASE ${CMAKE_CURRENT_BINARY_DIR}) endfunction() # If a task needs to run before any targets are finalized in the current directory # scope, call this function and pass the ID of that task as the argument. function(_qt_internal_delay_finalization_until_after defer_id) set_property(DIRECTORY APPEND PROPERTY qt_internal_finalizers_wait_for_ids "${defer_id}") endfunction() function(qt6_finalize_target target) set_property(TARGET ${target} PROPERTY _qt_expects_finalization FALSE) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.19") cmake_language(DEFER GET_CALL_IDS ids_queued) get_directory_property(wait_for_ids qt_internal_finalizers_wait_for_ids) while(wait_for_ids) list(GET wait_for_ids 0 id_to_wait_for) if(id_to_wait_for IN_LIST ids_queued) # Something else needs to run before we finalize targets. # Try again later by re-deferring ourselves, which effectively # puts us at the end of the current list of deferred actions. cmake_language(EVAL CODE "cmake_language(DEFER CALL ${CMAKE_CURRENT_FUNCTION} ${ARGV})") set_directory_properties(PROPERTIES qt_internal_finalizers_wait_for_ids "${wait_for_ids}" ) return() endif() list(POP_FRONT wait_for_ids) endwhile() # No other deferred tasks to wait for set_directory_properties(PROPERTIES qt_internal_finalizers_wait_for_ids "") endif() if(NOT TARGET "${target}") message(FATAL_ERROR "No target '${target}' found in current scope.") endif() get_target_property(is_finalized "${target}" _qt_is_finalized) if(is_finalized) message(AUTHOR_WARNING "Tried to call qt6_finalize_target twice on target '${target}'. " "Did you forget to specify MANUAL_FINALIZATION to qt6_add_executable, " "qt6_add_library or qt6_add_plugin?") return() endif() _qt_internal_expose_deferred_files_to_ide(${target}) get_target_property(target_type ${target} TYPE) get_target_property(is_android_executable "${target}" _qt_is_android_executable) if(target_type STREQUAL "EXECUTABLE" OR is_android_executable) _qt_internal_finalize_executable(${ARGV}) endif() set_target_properties(${target} PROPERTIES _qt_is_finalized TRUE) endfunction() function(_qt_internal_darwin_permission_finalizer target) get_target_property(plist_file "${target}" MACOSX_BUNDLE_INFO_PLIST) if(NOT plist_file) return() endif() foreach(plugin_target IN LISTS QT_ALL_PLUGINS_FOUND_BY_FIND_PACKAGE_permissions) set(versioned_plugin_target "${QT_CMAKE_EXPORT_NAMESPACE}::${plugin_target}") get_target_property(usage_descriptions ${versioned_plugin_target} _qt_info_plist_usage_descriptions) foreach(usage_description_key IN LISTS usage_descriptions) execute_process(COMMAND "/usr/libexec/PlistBuddy" -c "print ${usage_description_key}" "${plist_file}" OUTPUT_VARIABLE usage_description ERROR_VARIABLE plist_error) if(usage_description AND NOT plist_error) set_target_properties("${target}" PROPERTIES "_qt_has_${plugin_target}_usage_description" TRUE) qt6_import_plugins(${target} INCLUDE ${versioned_plugin_target}) endif() endforeach() endforeach() endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) function(qt_add_executable) qt6_add_executable(${ARGV}) endfunction() function(qt_finalize_target) qt6_finalize_target(${ARGV}) endfunction() # Kept for compatibility with Qt Creator 4.15 wizards function(qt_finalize_executable) qt6_finalize_target(${ARGV}) endfunction() endif() function(_qt_get_plugin_name_with_version target out_var) string(REGEX REPLACE "^Qt::(.+)" "Qt${QT_DEFAULT_MAJOR_VERSION}::\\1" qt_plugin_with_version "${target}") if(TARGET "${qt_plugin_with_version}") set("${out_var}" "${qt_plugin_with_version}" PARENT_SCOPE) else() set("${out_var}" "" PARENT_SCOPE) endif() endfunction() macro(_qt_import_plugin target plugin) set(_final_plugin_name "${plugin}") if(NOT TARGET "${plugin}") _qt_get_plugin_name_with_version("${plugin}" _qt_plugin_with_version_name) if(TARGET "${_qt_plugin_with_version_name}") set(_final_plugin_name "${_qt_plugin_with_version_name}") endif() endif() if(NOT TARGET "${_final_plugin_name}") message( "Warning: plug-in ${_final_plugin_name} is not known to the current Qt installation.") else() get_target_property(_plugin_class_name "${_final_plugin_name}" QT_PLUGIN_CLASS_NAME) if(_plugin_class_name) set_property(TARGET "${target}" APPEND PROPERTY QT_PLUGINS "${plugin}") endif() endif() endmacro() function(_qt_internal_disable_static_default_plugins target) set_target_properties(${target} PROPERTIES QT_DEFAULT_PLUGINS 0) endfunction() function(qt6_import_plugins target) cmake_parse_arguments(arg "NO_DEFAULT" "" "INCLUDE;EXCLUDE;INCLUDE_BY_TYPE;EXCLUDE_BY_TYPE" ${ARGN}) # Handle NO_DEFAULT if(${arg_NO_DEFAULT}) _qt_internal_disable_static_default_plugins("${target}") endif() # Handle INCLUDE foreach(plugin ${arg_INCLUDE}) _qt_import_plugin("${target}" "${plugin}") endforeach() # Handle EXCLUDE foreach(plugin ${arg_EXCLUDE}) set_property(TARGET "${target}" APPEND PROPERTY QT_NO_PLUGINS "${plugin}") endforeach() # Handle INCLUDE_BY_TYPE set(_current_type "") foreach(_arg ${arg_INCLUDE_BY_TYPE}) string(REGEX REPLACE "[-/]" "_" _plugin_type "${_arg}") list(FIND QT_ALL_PLUGIN_TYPES_FOUND_VIA_FIND_PACKAGE "${_plugin_type}" _has_plugin_type) if(${_has_plugin_type} GREATER_EQUAL 0) set(_current_type "${_plugin_type}") else() if("${_current_type}" STREQUAL "") message(FATAL_ERROR "qt_import_plugins: invalid syntax for INCLUDE_BY_TYPE") endif() # Check if passed plugin target name is a version-less one, and make a version-full # one. set_property(TARGET "${target}" APPEND PROPERTY "QT_PLUGINS_${_current_type}" "${_arg}") set_property(TARGET "${target}" APPEND PROPERTY "_qt_plugins_by_type" "${_arg}") _qt_get_plugin_name_with_version("${_arg}" qt_plugin_with_version) # TODO: Do we really need this check? We didn't have it in Qt5, and plugin targets # wrapped in genexes end up causing warnings, but we explicitly use GENEX_EVAL to # support them. if(NOT TARGET "${_arg}" AND NOT TARGET "${qt_plugin_with_version}") message("Warning: plug-in ${_arg} is not known to the current Qt installation.") endif() endif() endforeach() # Handle EXCLUDE_BY_TYPE foreach(_arg ${arg_EXCLUDE_BY_TYPE}) string(REGEX REPLACE "[-/]" "_" _plugin_type "${_arg}") set_property(TARGET "${target}" PROPERTY "QT_PLUGINS_${_plugin_type}" "-") endforeach() endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) function(qt_import_plugins) if(QT_DEFAULT_MAJOR_VERSION EQUAL 5) qt5_import_plugins(${ARGV}) elseif(QT_DEFAULT_MAJOR_VERSION EQUAL 6) qt6_import_plugins(${ARGV}) endif() endfunction() endif() # This function is currently in Technical Preview. It's signature may change or be removed entirely. function(qt6_set_finalizer_mode target) cmake_parse_arguments(arg "ENABLE;DISABLE" "" "MODES" ${ARGN}) if(NOT arg_ENABLE AND NOT arg_DISABLE) message(FATAL_ERROR "No option was specified whether to enable or disable the modes.") elseif(arg_ENABLE AND arg_DISABLE) message(FATAL_ERROR "Both ENABLE and DISABLE options were specified.") endif() if(NOT arg_MODES) message(FATAL_ERROR "No modes were specified in qt6_set_finalizer_mode() call.") endif() if(arg_ENABLE) set(value "TRUE") elseif(arg_DISABLE) set(value "FALSE") endif() foreach(mode ${arg_MODES}) __qt_internal_enable_finalizer_mode("${target}" "${mode}" "${value}") endforeach() endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) function(qt_set_finalizer_mode) qt6_set_finalizer_mode(${ARGV}) endfunction() endif() function(_qt_internal_assign_to_internal_targets_folder target) get_property(folder_name GLOBAL PROPERTY QT_TARGETS_FOLDER) if(NOT "${folder_name}" STREQUAL "") set_property(TARGET ${target} PROPERTY FOLDER "${folder_name}") endif() endfunction() function(_qt_internal_get_target_autogen_build_dir target out_var) get_property(target_autogen_build_dir TARGET ${target} PROPERTY AUTOGEN_BUILD_DIR) if(target_autogen_build_dir) set(${out_var} "${target_autogen_build_dir}" PARENT_SCOPE) else() get_property(target_binary_dir TARGET ${target} PROPERTY BINARY_DIR) set(${out_var} "${target_binary_dir}/${target}_autogen" PARENT_SCOPE) endif() endfunction() function(qt6_extract_metatypes target) get_target_property(existing_meta_types_file ${target} INTERFACE_QT_META_TYPES_BUILD_FILE) if (existing_meta_types_file) return() endif() set(args_option # TODO: Move this into a separate internal function, so it doesn't pollute the public one. # When given, metatypes files will be installed into the default Qt # metatypes folder. Only to be used by the Qt build. __QT_INTERNAL_INSTALL ) set(args_single # TODO: Move this into a separate internal function, so it doesn't pollute the public one. # Location where to install the metatypes file. Only used if # __QT_INTERNAL_INSTALL is given. It defaults to the # ${CMAKE_INSTALL_PREFIX}/${INSTALL_ARCHDATADIR}/metatypes directory. # Executable metatypes files are never installed. __QT_INTERNAL_INSTALL_DIR OUTPUT_FILES ) set(args_multi MANUAL_MOC_JSON_FILES ) cmake_parse_arguments(arg "${args_option}" "${args_single}" "${args_multi}" ${ARGN}) get_target_property(target_type ${target} TYPE) if (target_type STREQUAL "INTERFACE_LIBRARY") message(FATAL_ERROR "Meta types generation does not work on interface libraries") return() endif() if (CMAKE_VERSION VERSION_LESS "3.16.0") message(FATAL_ERROR "Meta types generation requires CMake >= 3.16") return() endif() get_target_property(target_binary_dir ${target} BINARY_DIR) set(type_list_file "${target_binary_dir}/meta_types/${target}_json_file_list.txt") set(type_list_file_manual "${target_binary_dir}/meta_types/${target}_json_file_list_manual.txt") set(target_autogen_build_dir "") _qt_internal_get_target_autogen_build_dir(${target} target_autogen_build_dir) get_target_property(uses_automoc ${target} AUTOMOC) set(automoc_args) set(automoc_dependencies) # Handle automoc generated data if (uses_automoc) # Tell automoc to output json files) set_property(TARGET "${target}" APPEND PROPERTY AUTOMOC_MOC_OPTIONS "--output-json" ) get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(NOT is_multi_config) set(cmake_autogen_cache_file "${target_binary_dir}/CMakeFiles/${target}_autogen.dir/ParseCache.txt") set(multi_config_args --cmake-autogen-include-dir-path "${target_autogen_build_dir}/include" ) else() set(cmake_autogen_cache_file "${target_binary_dir}/CMakeFiles/${target}_autogen.dir/ParseCache_$.txt") set(multi_config_args --cmake-autogen-include-dir-path "${target_autogen_build_dir}/include_$" "--cmake-multi-config") endif() set(cmake_autogen_info_file "${target_binary_dir}/CMakeFiles/${target}_autogen.dir/AutogenInfo.json") set (use_dep_files FALSE) if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.17") # Requires automoc changes present only in 3.17 if(CMAKE_GENERATOR STREQUAL "Ninja" OR CMAKE_GENERATOR STREQUAL "Ninja Multi-Config") set(use_dep_files TRUE) endif() endif() set(cmake_automoc_parser_timestamp "${type_list_file}.timestamp") if (NOT use_dep_files) # When a project is configured with a Visual Studio generator, CMake's # cmQtAutoGenInitializer::InitAutogenTarget() can take one of two code paths on how to # handle AUTOMOC rules. # It either creates a ${target}_autogen custom target or uses PRE_BUILD build events. # # The latter in considered an optimization and is used by CMake when possible. # Unfortunately that breaks our add_dependency call because we expect on _autogen target # to always exist. # # Ensure the PRE_BUILD path is not taken by generating a dummy header file and adding it # as a source file to the target. This causes the file to be added to # cmQtAutoGenInitializer::AutogenTarget.DependFiles, which disables the PRE_BUILD path. if(CMAKE_GENERATOR MATCHES "Visual Studio") # The file name should be target specific, but still short, so we don't hit path # length issues. string(MAKE_C_IDENTIFIER "ddf_${target}" dummy_dependency_file) set(dummy_out_file "${CMAKE_CURRENT_BINARY_DIR}/${dummy_dependency_file}.h") # The content shouldn't be empty so we don't trigger AUTOMOC warnings about it. file(GENERATE OUTPUT "${dummy_out_file}" CONTENT "//") set_source_files_properties("${dummy_out_file}" PROPERTIES GENERATED TRUE SKIP_AUTOGEN OFF) target_sources("${target}" PRIVATE "${dummy_out_file}") endif() add_custom_target(${target}_automoc_json_extraction DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::cmake_automoc_parser BYPRODUCTS ${type_list_file} "${cmake_automoc_parser_timestamp}" COMMAND ${QT_CMAKE_EXPORT_NAMESPACE}::cmake_automoc_parser --cmake-autogen-cache-file "${cmake_autogen_cache_file}" --cmake-autogen-info-file "${cmake_autogen_info_file}" --output-file-path "${type_list_file}" --timestamp-file-path "${cmake_automoc_parser_timestamp}" ${multi_config_args} COMMENT "Running AUTOMOC file extraction for target ${target}" COMMAND_EXPAND_LISTS ) add_dependencies(${target}_automoc_json_extraction ${target}_autogen) _qt_internal_assign_to_internal_targets_folder(${target}_automoc_json_extraction) else() set(use_better_automoc_graph FALSE) if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.28.0" AND CMAKE_CROSS_CONFIGS) if(DEFINED QT_USE_BETTER_AUTOMOC_GRAPH) set(use_better_automoc_graph ${QT_USE_BETTER_AUTOMOC_GRAPH}) else() set(use_better_automoc_graph TRUE) endif() endif() if(use_better_automoc_graph) set(cmake_autogen_timestamp_file "${target_autogen_build_dir}/timestamp_$") else() set(cmake_autogen_timestamp_file "${target_autogen_build_dir}/timestamp") endif() add_custom_command(OUTPUT ${type_list_file} DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::cmake_automoc_parser ${cmake_autogen_timestamp_file} BYPRODUCTS "${cmake_automoc_parser_timestamp}" COMMAND ${QT_CMAKE_EXPORT_NAMESPACE}::cmake_automoc_parser --cmake-autogen-cache-file "${cmake_autogen_cache_file}" --cmake-autogen-info-file "${cmake_autogen_info_file}" --output-file-path "${type_list_file}" --timestamp-file-path "${cmake_automoc_parser_timestamp}" ${multi_config_args} COMMENT "Running AUTOMOC file extraction for target ${target}" COMMAND_EXPAND_LISTS VERBATIM ) endif() set(automoc_args "@${type_list_file}") set(automoc_dependencies "${type_list_file}") endif() set(manual_args) set(manual_dependencies) if(arg_MANUAL_MOC_JSON_FILES) list(REMOVE_DUPLICATES arg_MANUAL_MOC_JSON_FILES) file(GENERATE OUTPUT ${type_list_file_manual} CONTENT "$,\n>" ) list(APPEND manual_dependencies ${arg_MANUAL_MOC_JSON_FILES} ${type_list_file_manual}) set(manual_args "@${type_list_file_manual}") endif() if (NOT manual_args AND NOT automoc_args) message(FATAL_ERROR "Metatype generation requires either the use of AUTOMOC or a manual list of generated json files") endif() if (CMAKE_BUILD_TYPE AND NOT is_multi_config) string(TOLOWER ${target}_${CMAKE_BUILD_TYPE} target_lowercase) else() string(TOLOWER ${target} target_lowercase) endif() set(metatypes_file_name "qt6${target_lowercase}_metatypes.json") set(metatypes_file "${target_binary_dir}/meta_types/${metatypes_file_name}") set(metatypes_file_gen "${target_binary_dir}/meta_types/${metatypes_file_name}.gen") set(metatypes_dep_file_name "qt6${target_lowercase}_metatypes_dep.txt") set(metatypes_dep_file "${target_binary_dir}/meta_types/${metatypes_dep_file_name}") # Due to generated source file dependency rules being tied to the directory # scope in which they are created it is not possible for other targets which # are defined in a separate scope to see these rules. This leads to failures # in locating the generated source files. # To work around this we write a dummy file to disk to make sure targets # which link against the current target do not produce the error. This dummy # file is then replaced with the contents of the generated file during # build. if (NOT EXISTS ${metatypes_file}) file(MAKE_DIRECTORY "${target_binary_dir}/meta_types") file(TOUCH ${metatypes_file}) endif() add_custom_command( OUTPUT ${metatypes_file_gen} BYPRODUCTS ${metatypes_file} DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::moc ${automoc_dependencies} ${manual_dependencies} COMMAND ${QT_CMAKE_EXPORT_NAMESPACE}::moc -o ${metatypes_file_gen} --collect-json ${automoc_args} ${manual_args} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${metatypes_file_gen} ${metatypes_file} COMMENT "Running moc --collect-json for target ${target}" VERBATIM ) if(CMAKE_GENERATOR MATCHES "Unix Makefiles") # Work around https://gitlab.kitware.com/cmake/cmake/-/issues/19005 to trigger the command # that generates ${metatypes_file}. add_custom_command( OUTPUT ${metatypes_file} DEPENDS ${metatypes_file_gen} COMMAND ${CMAKE_COMMAND} -E true VERBATIM ) endif() # We can't rely on policy CMP0118 since user project controls it set(scope_args) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18") set(scope_args TARGET_DIRECTORY ${target}) endif() set_source_files_properties(${metatypes_file_gen} ${metatypes_file} ${scope_args} PROPERTIES GENERATED TRUE ) # We still need to add this file as a source of the target, otherwise the file # rule above is not triggered. INTERFACE_SOURCES do not properly register # as dependencies to build the current target. # TODO: Can we pass ${metatypes_file} instead of ${metatypes_file_gen} as a source? # TODO: Do we still need the _gen variant at all? target_sources(${target} PRIVATE ${metatypes_file_gen}) set_source_files_properties(${metatypes_file} ${scope_args} PROPERTIES HEADER_FILE_ONLY TRUE ) set_target_properties(${target} PROPERTIES INTERFACE_QT_MODULE_HAS_META_TYPES YES INTERFACE_QT_META_TYPES_BUILD_FILE "${metatypes_file}" ) # Set up consumption of files via INTERFACE_SOURCES. set(consumes_metatypes "$>") set(metatypes_file_genex_build "$>" ) target_sources(${target} INTERFACE ${metatypes_file_genex_build}) if(arg_OUTPUT_FILES) set(${arg_OUTPUT_FILES} "${metatypes_file}" PARENT_SCOPE) endif() # Check whether the generated json file needs to be installed. # Executable metatypes.json files should not be installed. Qt non-prefix builds should also # not install the files. set(should_install FALSE) if(NOT target_type STREQUAL "EXECUTABLE" AND arg___QT_INTERNAL_INSTALL) set(should_install TRUE) endif() # Automatically fill default install args when not specified. if(NOT arg___QT_INTERNAL_INSTALL_DIR) # INSTALL_ARCHDATADIR is not set when QtBuildInternals is not loaded # (when not doing a Qt build). Default to a hardcoded location for user # projects (will likely be wrong). if(INSTALL_ARCHDATADIR) set(install_dir "${INSTALL_ARCHDATADIR}/metatypes") else() set(install_dir "lib/metatypes") endif() else() set(install_dir "${arg___QT_INTERNAL_INSTALL_DIR}") endif() if(should_install) set(metatypes_file_install_path "${install_dir}/${metatypes_file_name}") set(metatypes_file_install_path_genex "$/${metatypes_file_install_path}") set(metatypes_file_genex_install "$>" ) target_sources(${target} INTERFACE ${metatypes_file_genex_install}) install(FILES "${metatypes_file}" DESTINATION "${install_dir}") endif() endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) function(qt_extract_metatypes) qt6_extract_metatypes(${ARGV}) cmake_parse_arguments(PARSE_ARGV 0 arg "" "OUTPUT_FILES" "") if(arg_OUTPUT_FILES) set(${arg_OUTPUT_FILES} "${${arg_OUTPUT_FILES}}" PARENT_SCOPE) endif() endfunction() endif() # Generate Win32 RC files for a target. All entries in the RC file are generated # from target properties: # # QT_TARGET_COMPANY_NAME: RC Company name # QT_TARGET_DESCRIPTION: RC File Description # QT_TARGET_VERSION: RC File and Product Version # QT_TARGET_COPYRIGHT: RC LegalCopyright # QT_TARGET_PRODUCT_NAME: RC ProductName # QT_TARGET_COMMENTS: RC Comments # QT_TARGET_ORIGINAL_FILENAME: RC Original FileName # QT_TARGET_TRADEMARKS: RC LegalTrademarks # QT_TARGET_INTERNALNAME: RC InternalName # QT_TARGET_RC_ICONS: List of paths to icon files # # If you do not wish to auto-generate rc files, it's possible to provide your # own RC file by setting the property QT_TARGET_WINDOWS_RC_FILE with a path to # an existing rc file. function(_qt_internal_generate_win32_rc_file target) set(prohibited_target_types INTERFACE_LIBRARY STATIC_LIBRARY OBJECT_LIBRARY) get_target_property(target_type ${target} TYPE) if(target_type IN_LIST prohibited_target_types) return() endif() get_target_property(target_binary_dir ${target} BINARY_DIR) get_target_property(target_rc_file ${target} QT_TARGET_WINDOWS_RC_FILE) get_target_property(target_version ${target} QT_TARGET_VERSION) if (NOT target_rc_file AND NOT target_version) return() endif() if(MSVC) set(extra_rc_flags "-c65001 -DWIN32 -nologo") else() set(extra_rc_flags) endif() if (target_rc_file) # Use the provided RC file target_sources(${target} PRIVATE "${target_rc_file}") set_property(SOURCE ${target_rc_file} PROPERTY COMPILE_FLAGS "${extra_rc_flags}") else() # Generate RC File set(rc_file_output "${target_binary_dir}/") if(QT_GENERATOR_IS_MULTI_CONFIG) string(APPEND rc_file_output "$/") endif() string(APPEND rc_file_output "${target}_resource.rc") set(target_rc_file "${rc_file_output}") set(company_name "") get_target_property(target_company_name ${target} QT_TARGET_COMPANY_NAME) if (target_company_name) set(company_name "${target_company_name}") endif() set(file_description "") get_target_property(target_description ${target} QT_TARGET_DESCRIPTION) if (target_description) set(file_description "${target_description}") endif() set(legal_copyright "") get_target_property(target_copyright ${target} QT_TARGET_COPYRIGHT) if (target_copyright) set(legal_copyright "${target_copyright}") endif() set(product_name "") get_target_property(target_product_name ${target} QT_TARGET_PRODUCT_NAME) if (target_product_name) set(product_name "${target_product_name}") else() set(product_name "${target}") endif() set(comments "") get_target_property(target_comments ${target} QT_TARGET_COMMENTS) if (target_comments) set(comments "${target_comments}") endif() set(legal_trademarks "") get_target_property(target_trademarks ${target} QT_TARGET_TRADEMARKS) if (target_trademarks) set(legal_trademarks "${target_trademarks}") endif() set(product_version "") if (target_version) if(target_version MATCHES "[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+") # nothing to do elseif(target_version MATCHES "[0-9]+\\.[0-9]+\\.[0-9]+") set(target_version "${target_version}.0") elseif(target_version MATCHES "[0-9]+\\.[0-9]+") set(target_version "${target_version}.0.0") elseif (target_version MATCHES "[0-9]+") set(target_version "${target_version}.0.0.0") else() message(FATAL_ERROR "Invalid version format: '${target_version}'") endif() set(product_version "${target_version}") else() set(product_version "0.0.0.0") endif() set(file_version "${product_version}") string(REPLACE "." "," version_comma ${product_version}) set(original_file_name "$") get_target_property(target_original_file_name ${target} QT_TARGET_ORIGINAL_FILENAME) if (target_original_file_name) set(original_file_name "${target_original_file_name}") endif() set(internal_name "") get_target_property(target_internal_name ${target} QT_TARGET_INTERNALNAME) if (target_internal_name) set(internal_name "${target_internal_name}") endif() set(icons "") get_target_property(target_icons ${target} QT_TARGET_RC_ICONS) if (target_icons) set(index 1) foreach( icon IN LISTS target_icons) string(APPEND icons "IDI_ICON${index} ICON \"${icon}\"\n") math(EXPR index "${index} +1") endforeach() endif() set(target_file_type "VFT_DLL") if(target_type STREQUAL "EXECUTABLE") set(target_file_type "VFT_APP") endif() set(contents "#include ${icons} VS_VERSION_INFO VERSIONINFO FILEVERSION ${version_comma} PRODUCTVERSION ${version_comma} FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS VS_FF_DEBUG #else FILEFLAGS 0x0L #endif FILEOS VOS_NT_WINDOWS32 FILETYPE ${target_file_type} FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK \"StringFileInfo\" BEGIN BLOCK \"040904b0\" BEGIN VALUE \"CompanyName\", \"${company_name}\" VALUE \"FileDescription\", \"${file_description}\" VALUE \"FileVersion\", \"${file_version}\" VALUE \"LegalCopyright\", \"${legal_copyright}\" VALUE \"OriginalFilename\", \"${original_file_name}\" VALUE \"ProductName\", \"${product_name}\" VALUE \"ProductVersion\", \"${product_version}\" VALUE \"Comments\", \"${comments}\" VALUE \"LegalTrademarks\", \"${legal_trademarks}\" VALUE \"InternalName\", \"${internal_name}\" END END BLOCK \"VarFileInfo\" BEGIN VALUE \"Translation\", 0x0409, 1200 END END /* End of Version info */\n" ) # We can't use the output of file generate as source so we work around # this by generating the file under a different name and then copying # the file in place using add custom command. file(GENERATE OUTPUT "${rc_file_output}.tmp" CONTENT "${contents}" ) if(QT_GENERATOR_IS_MULTI_CONFIG) set(cfgs ${CMAKE_CONFIGURATION_TYPES}) set(outputs "") foreach(cfg ${cfgs}) string(REPLACE "$" "${cfg}" expanded_rc_file_output "${rc_file_output}") list(APPEND outputs "${expanded_rc_file_output}") endforeach() else() set(cfgs "${CMAKE_BUILD_TYPE}") set(outputs "${rc_file_output}") endif() # We would like to do the following: # target_sources(${target} PRIVATE "$<$:${output}>") # # However, https://gitlab.kitware.com/cmake/cmake/-/issues/20682 doesn't let us do that # in CMake 3.19 and earlier. # We can do it in CMake 3.20 and later. # And we have to do it with CMake 3.21.0 to avoid a different issue # https://gitlab.kitware.com/cmake/cmake/-/issues/22436 # # So use the object lib work around for <= 3.19 and target_sources directly for later # versions. set(use_obj_lib FALSE) set(end_target "${target}") if(CMAKE_VERSION VERSION_LESS 3.20) set(use_obj_lib TRUE) set(end_target "${target}_rc") add_library(${target}_rc OBJECT "${output}") target_link_libraries(${target} PRIVATE $) endif() set(scope_args) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18") set(scope_args TARGET_DIRECTORY ${end_target}) endif() while(outputs) list(POP_FRONT cfgs cfg) list(POP_FRONT outputs output) set(input "${output}.tmp") add_custom_command(OUTPUT "${output}" DEPENDS "${input}" COMMAND ${CMAKE_COMMAND} -E copy_if_different "${input}" "${output}" VERBATIM ) # We can't rely on policy CMP0118 since user project controls it set_source_files_properties(${output} ${scope_args} PROPERTIES GENERATED TRUE COMPILE_FLAGS "${extra_rc_flags}" ) target_sources(${end_target} PRIVATE "$<$:${output}>") endwhile() endif() endfunction() # Generate Win32 longPathAware RC and Manifest files for a target. # MSVC needs the manifest file as part of target_sources. MinGW the RC file. # function(_qt_internal_generate_longpath_win32_rc_file_and_manifest target) set(prohibited_target_types INTERFACE_LIBRARY STATIC_LIBRARY OBJECT_LIBRARY) get_target_property(target_type ${target} TYPE) if(target_type IN_LIST prohibited_target_types) return() endif() get_target_property(target_binary_dir ${target} BINARY_DIR) # Generate manifest set(target_mn_filename "${target}_longpath.manifest") set(mn_file_output "${target_binary_dir}/${target_mn_filename}") set(mn_contents [=[ true ]=]) file(GENERATE OUTPUT "${mn_file_output}" CONTENT "${mn_contents}") # Generate RC File set(rc_file_output "${target_binary_dir}/${target}_longpath.rc") set(rc_contents "1 /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 24 /* RT_MANIFEST */ ${target_mn_filename}") file(GENERATE OUTPUT "${rc_file_output}" CONTENT "${rc_contents}") if (MINGW) set(outputs "${rc_file_output}") endif() list(APPEND outputs "${mn_file_output}") foreach(output IN LISTS outputs) # Needed for CMake versions < 3.19 set_source_files_properties(${output} PROPERTIES GENERATED TRUE) target_sources(${target} PRIVATE "${output}") endforeach() endfunction() 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}") if(IS_ABSOLUTE "${file}") message(FATAL_ERROR "The source file '${file}' was specified with an absolute path and is used in a Qt " "resource. Please set the QT_RESOURCE_ALIAS property on that source file to a " "relative path to make the file properly accessible via the resource system." ) endif() endif() set(${output_alias} ${alias} PARENT_SCOPE) endfunction() # Performs linking and propagation of the specified objects via the target's usage requirements. # The objects may be given as generator expression. # # Arguments: # EXTRA_CONDITIONS # Conditions to be checked before linking the object files to the end-point executable. # EXTRA_TARGET_LINK_LIBRARIES_CONDITIONS # Conditions for the target_link_libraries call. # EXTRA_TARGET_SOURCES_CONDITIONS # Conditions for the target_sources call. function(__qt_internal_propagate_object_files target objects) set(options "") set(single_args "") set(extra_conditions_args EXTRA_CONDITIONS EXTRA_TARGET_LINK_LIBRARIES_CONDITIONS EXTRA_TARGET_SOURCES_CONDITIONS ) set(multi_args ${extra_conditions_args}) cmake_parse_arguments(arg "${options}" "${single_args}" "${multi_args}" ${ARGN}) # Collect additional conditions. foreach(arg IN LISTS extra_conditions_args) string(TOLOWER "${arg}" lcvar) if(arg_${arg}) list(JOIN arg_${arg} "," ${lcvar}) else() set(${lcvar} "$") endif() endforeach() # Do not litter the static libraries set(not_static_condition "$,STATIC_LIBRARY>>" ) # Check if link order matters for the Platform. set(platform_link_order_property "$" ) set(platform_link_order_condition "$" ) # Check if link options are propagated according to CMP0099 # In user builds the _qt_cmp0099_policy_check is set to FALSE or $ # depending on the used CMake version. # See __qt_internal_check_cmp0099_available for details. set(cmp0099_policy_check_property "$" ) set(link_objects_using_link_options_condition "$>" ) # Collect link conditions for the target_sources call. string(JOIN "" target_sources_genex "$<" "$," "${extra_target_sources_conditions}," "${extra_conditions}" ">" ":${objects}>" ) target_sources(${target} INTERFACE "${target_sources_genex}" ) # Collect link conditions for the target_link_options call. string(JOIN "" target_link_options_genex "$<" "$" ":${objects}>" ) # target_link_options works well since CMake 3.17 which has policy CMP0099 set to NEW for the # minimum required CMake version greater than or equal to 3.17. The default is OLD. See # https://cmake.org/cmake/help/git-master/policy/CMP0099.html for details. # This provides yet another way of linking object libraries if user sets the policy to NEW # before calling find_package(Qt...). target_link_options(${target} INTERFACE "${target_link_options_genex}" ) # Collect link conditions for the target_link_libraries call. string(JOIN "" target_link_libraries_genex "$<" "$," "${extra_target_link_libraries_conditions}," "${extra_conditions}" ">" ":${objects}>" ) target_link_libraries(${target} INTERFACE "${target_link_libraries_genex}" ) endfunction() # Performs linking and propagation of the object library via the target's usage requirements. # Arguments: # NO_LINK_OBJECT_LIBRARY_REQUIREMENTS_TO_TARGET skip linking of ${object_library} to ${target}, only # propagate $ by linking it to ${target}. It's useful in case # if ${object_library} depends on the ${target}. E.g. resource libraries depend on the Core # library so linking them back to Core will cause a CMake error. # # EXTRA_CONDITIONS object library specific conditions to be checked before link the object library # to the end-point executable. function(__qt_internal_propagate_object_library target object_library) set(options NO_LINK_OBJECT_LIBRARY_REQUIREMENTS_TO_TARGET) set(single_args "") set(multi_args EXTRA_CONDITIONS) cmake_parse_arguments(arg "${options}" "${single_args}" "${multi_args}" ${ARGN}) get_target_property(is_imported ${object_library} IMPORTED) if(NOT is_imported) target_link_libraries(${object_library} PRIVATE ${QT_CMAKE_EXPORT_NAMESPACE}::Platform) _qt_internal_copy_dependency_properties(${object_library} ${target} PRIVATE_ONLY) endif() # After internal discussion we decided to not rely on the linker order that CMake # offers, until CMake provides the guaranteed linking order that suites our needs in a # future CMake version. # All object libraries mark themselves with the _is_qt_propagated_object_library property. # Using a finalizer approach we walk through the target dependencies and look for libraries # using the _is_qt_propagated_object_library property. Then, objects of the collected libraries # are moved to the beginning of the linker line using target_sources. # # Note: target_link_libraries works well with linkers other than ld. If user didn't enforce # a finalizer we rely on linker to resolve circular dependencies between objects and static # libraries. set_property(TARGET ${object_library} PROPERTY _is_qt_propagated_object_library TRUE) if(NOT is_imported) set_property(TARGET ${object_library} APPEND PROPERTY EXPORT_PROPERTIES _is_qt_propagated_object_library ) endif() # Keep the implicit linking if finalizers are not used. set(not_finalizer_mode_condition "$>>" ) # Use TARGET_NAME to have the correct namespaced name in the exports. set(objects "$>") __qt_internal_propagate_object_files(${target} ${objects} EXTRA_CONDITIONS ${arg_EXTRA_CONDITIONS} EXTRA_TARGET_SOURCES_CONDITIONS ${not_finalizer_mode_condition} EXTRA_TARGET_LINK_LIBRARIES_CONDITIONS ${not_finalizer_mode_condition} ) if(NOT arg_NO_LINK_OBJECT_LIBRARY_REQUIREMENTS_TO_TARGET) # It's necessary to link the object library target, since we want to pass the object library # dependencies to the 'target'. Interface linking doesn't add the objects of the library to # the end-point linker line but propagates all the dependencies of the object_library added # before or AFTER the line below. target_link_libraries(${target} INTERFACE ${object_library}) endif() 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) get_target_property(resource_count ${target} _qt_generated_resource_target_count) if(NOT resource_count) set(resource_count "0") endif() math(EXPR resource_count "${resource_count} + 1") set_target_properties(${target} PROPERTIES _qt_generated_resource_target_count ${resource_count}) set(resource_target "${target}_resources_${resource_count}") add_library("${resource_target}" OBJECT "${generated_source_code}") target_compile_definitions("${resource_target}" PRIVATE "$" ) _qt_internal_set_up_static_runtime_library("${resource_target}") # Special handling is required for the Core library resources. The linking of the Core # library to the resources adds a circular dependency. This leads to the wrong # objects/library order in the linker command line, since the Core library target is # resolved first. if(NOT target STREQUAL "Core") target_link_libraries(${resource_target} INTERFACE ${QT_CMAKE_EXPORT_NAMESPACE}::Core) endif() set_property(TARGET ${resource_target} APPEND PROPERTY _qt_resource_name ${resource_name}) # Save the path to the generated source file, relative to the the current build dir. # The path will be used in static library prl file generation to ensure qmake links # against the installed resource object files. # Example saved path: # .rcc/qrc_qprintdialog.cpp file(RELATIVE_PATH generated_cpp_file_relative_path "${CMAKE_CURRENT_BINARY_DIR}" "${generated_source_code}") set_property(TARGET ${resource_target} APPEND PROPERTY _qt_resource_generated_cpp_relative_path "${generated_cpp_file_relative_path}") if(target STREQUAL "Core") set(skip_direct_linking NO_LINK_OBJECT_LIBRARY_REQUIREMENTS_TO_TARGET) endif() __qt_internal_propagate_object_library(${target} ${resource_target} ${skip_direct_linking} ) set(${output_generated_target} "${resource_target}" PARENT_SCOPE) else() set(${output_generated_target} "" PARENT_SCOPE) target_sources(${target} PRIVATE ${generated_source_code}) endif() endfunction() # Make file visible in IDEs. # Targets that are finalized add the file as HEADER_FILE_ONLY in the finalizer. # Targets that are not finalized add the file under a ${target}_other_files target. function(_qt_internal_expose_source_file_to_ide target file) get_target_property(target_expects_finalization ${target} _qt_expects_finalization) if(target_expects_finalization AND CMAKE_VERSION VERSION_GREATER_EQUAL "3.19") set_property(TARGET ${target} APPEND PROPERTY _qt_deferred_files ${file}) return() endif() # Fallback for targets that are not finalized: Create fake target under which the file is added. set(ide_target ${target}_other_files) if(NOT TARGET ${ide_target}) add_custom_target(${ide_target} SOURCES "${file}") # The new Xcode build system requires a common target to drive the generation of files, # otherwise project configuration fails. # By adding ${target} as a dependency of ${target}_other_files, # it becomes the common target, so project configuration succeeds. if(CMAKE_GENERATOR STREQUAL "Xcode") add_dependencies(${ide_target} ${target}) endif() else() set_property(TARGET ${ide_target} APPEND PROPERTY SOURCES "${file}") endif() set(scope_args) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18") set(scope_args TARGET_DIRECTORY "${target}") endif() get_source_file_property( target_dependency "${file}" ${scope_args} _qt_resource_target_dependency) if(target_dependency) if(NOT TARGET "${target_dependency}") message(FATAL_ERROR "Target dependency on source file ${file} is not a cmake target.") endif() add_dependencies(${ide_target} ${target_dependency}) endif() endfunction() # Called by the target finalizer. # Adds the files that were added to _qt_deferred_files to SOURCES. # Sets HEADER_FILES_ONLY if they did not exist yet in SOURCES. function(_qt_internal_expose_deferred_files_to_ide target) get_target_property(new_sources ${target} _qt_deferred_files) if(NOT new_sources) return() endif() set(new_sources_real "") foreach(f IN LISTS new_sources) get_filename_component(realf "${f}" REALPATH) list(APPEND new_sources_real ${realf}) endforeach() set(filtered_new_sources "") get_target_property(target_source_dir ${target} SOURCE_DIR) get_filename_component(target_source_dir "${target_source_dir}" REALPATH) get_target_property(existing_sources ${target} SOURCES) if(existing_sources) set(existing_sources_real "") set(realf "") foreach(f IN LISTS existing_sources) get_filename_component(realf "${f}" REALPATH BASE_DIR ${target_source_dir}) list(APPEND existing_sources_real ${realf}) endforeach() list(LENGTH new_sources max_i) math(EXPR max_i "${max_i} - 1") foreach(i RANGE 0 ${max_i}) list(GET new_sources_real ${i} realf) if(NOT realf IN_LIST existing_sources_real) list(GET new_sources ${i} f) list(APPEND filtered_new_sources ${f}) endif() endforeach() endif() if("${filtered_new_sources}" STREQUAL "") return() endif() target_sources(${target} PRIVATE ${filtered_new_sources}) set(scope_args) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18") set(scope_args TARGET_DIRECTORY "${target}") endif() set_source_files_properties(${filtered_new_sources} ${scope_args} PROPERTIES HEADER_FILE_ONLY ON) endfunction() # # Process resources via file path instead of QRC files. Behind the # scenes, it will generate a qrc file. # # 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_source_files_properties() command. # # When using this command with static libraries, one or more special targets # will be generated. Should you wish to perform additional processing on these # targets pass a value to the OUTPUT_TARGETS parameter. # function(_qt_internal_process_resource target resourceName) cmake_parse_arguments(rcc "BIG_RESOURCES" "PREFIX;LANG;BASE;OUTPUT_TARGETS;DESTINATION" "FILES;OPTIONS" ${ARGN}) if("${rcc_OPTIONS}" MATCHES "-binary") set(isBinary TRUE) if(rcc_BIG_RESOURCES) message(FATAL_ERROR "BIG_RESOURCES cannot be used together with the -binary option.") endif() endif() if(rcc_BIG_RESOURCES AND CMAKE_GENERATOR STREQUAL "Xcode" AND IOS) message(WARNING "Due to CMake limitations, the BIG_RESOURCES option can't be used when building " "for iOS. " "See https://bugreports.qt.io/browse/QTBUG-103497 for details. " "Falling back to using regular resources. " ) set(rcc_BIG_RESOURCES OFF) endif() if(rcc_BIG_RESOURCES AND CMAKE_VERSION VERSION_LESS "3.17") message(WARNING "The BIG_RESOURCES option does not work reliably with CMake < 3.17. " "Consider upgrading to a more recent CMake version or disable the BIG_RESOURCES " "option for older CMake versions." ) endif() string(REPLACE "/" "_" resourceName ${resourceName}) string(REPLACE "." "_" resourceName ${resourceName}) set(resource_files "") # Strip the ending slashes from the file_path. If paths contain slashes in the end # set/get source properties works incorrect and may have the same QT_RESOURCE_ALIAS # for two different paths. See https://gitlab.kitware.com/cmake/cmake/-/issues/23212 # for details. foreach(file_path IN LISTS rcc_FILES) if(file_path MATCHES "(.+)/$") set(file_path "${CMAKE_MATCH_1}") endif() list(APPEND resource_files ${file_path}) endforeach() if(NOT "${rcc_BASE}" STREQUAL "") get_filename_component(abs_base "${rcc_BASE}" ABSOLUTE) foreach(file_path IN LISTS resource_files) get_source_file_property(alias "${file_path}" QT_RESOURCE_ALIAS) if(alias STREQUAL "NOTFOUND") get_filename_component(abs_file "${file_path}" ABSOLUTE) file(RELATIVE_PATH rel_file "${abs_base}" "${abs_file}") set_property(SOURCE "${file_path}" PROPERTY QT_RESOURCE_ALIAS "${rel_file}") endif() endforeach() endif() if(ANDROID) if(COMMAND _qt_internal_collect_qml_root_paths) _qt_internal_collect_qml_root_paths(${target} ${resource_files}) endif() endif() if(NOT rcc_PREFIX) get_target_property(rcc_PREFIX ${target} QT_RESOURCE_PREFIX) if (NOT rcc_PREFIX) set(rcc_PREFIX "/") endif() endif() if (NOT resource_files) if (rcc_OUTPUT_TARGETS) set(${rcc_OUTPUT_TARGETS} "" PARENT_SCOPE) endif() return() endif() set(generatedResourceFile "${CMAKE_CURRENT_BINARY_DIR}/.rcc/${resourceName}.qrc") _qt_internal_expose_source_file_to_ide(${target} ${generatedResourceFile}) # Generate .qrc file: # set(qrcContents "\n \n") set(resource_dependencies) foreach(file IN LISTS resource_files) __qt_get_relative_resource_path_for_file(file_resource_path ${file}) if (NOT IS_ABSOLUTE ${file}) set(file "${CMAKE_CURRENT_SOURCE_DIR}/${file}") endif() get_property(is_empty SOURCE ${file} PROPERTY QT_DISCARD_FILE_CONTENTS) ### FIXME: escape file paths to be XML conform # ... string(APPEND qrcContents " ${file}\n") list(APPEND files "${file}") set(scope_args) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18") set(scope_args TARGET_DIRECTORY ${target}) endif() get_source_file_property( target_dependency ${file} ${scope_args} _qt_resource_target_dependency) # The target dependency code path does not take care of rebuilds when ${file} # is touched. Limit its usage to the Xcode generator to avoid the Xcode common # dependency issue. # TODO: Figure out how to avoid the issue on Xcode, while also enabling proper # dependency tracking when ${file} is touched. if(target_dependency AND CMAKE_GENERATOR STREQUAL "Xcode") if(NOT TARGET ${target_dependency}) message(FATAL_ERROR "Target dependency on resource file ${file} is not a cmake target.") endif() list(APPEND resource_dependencies ${target_dependency}) else() list(APPEND resource_dependencies ${file}) endif() _qt_internal_expose_source_file_to_ide(${target} "${file}") endforeach() # string(APPEND qrcContents " \n\n") set(template_file "${__qt_core_macros_module_base_dir}/Qt6CoreConfigureFileTemplate.in") set(qt_core_configure_file_contents "${qrcContents}") configure_file("${template_file}" "${generatedResourceFile}") set(rccArgs --name "${resourceName}" "${generatedResourceFile}") set(rccArgsAllPasses "") if(rcc_OPTIONS) list(APPEND rccArgsAllPasses ${rcc_OPTIONS}) endif() # When cross-building, we use host tools to generate target code. If the host rcc was compiled # with zstd support, it expects the target QtCore to be able to decompress zstd compressed # content. This might be true with qmake where host tools are built as part of the # cross-compiled Qt, but with CMake we build tools separate from the cross-compiled Qt. # If the target does not support zstd (feature is disabled), tell rcc not to generate # zstd related code. if(NOT QT_FEATURE_zstd) list(APPEND rccArgsAllPasses "--no-zstd") endif() set_property(SOURCE "${generatedResourceFile}" PROPERTY SKIP_AUTOGEN ON) # Set output file name for rcc command if(isBinary) set(generatedOutfile "${CMAKE_CURRENT_BINARY_DIR}/${resourceName}.rcc") if(rcc_DESTINATION) # Add .rcc suffix if it's not specified by user get_filename_component(destinationRccExt "${rcc_DESTINATION}" LAST_EXT) if("${destinationRccExt}" STREQUAL ".rcc") set(generatedOutfile "${rcc_DESTINATION}") else() set(generatedOutfile "${rcc_DESTINATION}.rcc") endif() endif() elseif(rcc_BIG_RESOURCES) set(generatedOutfile "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qrc_${resourceName}_tmp.cpp") else() set(generatedOutfile "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qrc_${resourceName}.cpp") endif() set(pass_msg) if(rcc_BIG_RESOURCES) list(PREPEND rccArgs --pass 1) set(pass_msg " pass 1") endif() list(PREPEND rccArgs --output "${generatedOutfile}") # Process .qrc file: add_custom_command(OUTPUT "${generatedOutfile}" COMMAND "${QT_CMAKE_EXPORT_NAMESPACE}::rcc" ${rccArgs} ${rccArgsAllPasses} DEPENDS ${resource_dependencies} ${generatedResourceFile} "${QT_CMAKE_EXPORT_NAMESPACE}::rcc" COMMENT "Running rcc${pass_msg} for resource ${resourceName}" VERBATIM) if(isBinary) # Add generated .rcc target to 'all' set add_custom_target(binary_resource_${resourceName} ALL DEPENDS "${generatedOutfile}") return() endif() # We can't rely on policy CMP0118 since user project controls it. # We also want SKIP_AUTOGEN known in the target's scope, where we can. set(scope_args) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18") set(scope_args TARGET_DIRECTORY ${target}) endif() set_source_files_properties(${generatedOutfile} ${scope_args} PROPERTIES SKIP_AUTOGEN TRUE GENERATED TRUE SKIP_UNITY_BUILD_INCLUSION TRUE SKIP_PRECOMPILE_HEADERS TRUE ) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.27") set_source_files_properties(${generatedOutfile} ${scope_args} PROPERTIES SKIP_LINTING ON) endif() get_target_property(target_source_dir ${target} SOURCE_DIR) if(NOT target_source_dir STREQUAL CMAKE_CURRENT_SOURCE_DIR) # We have to create a separate target in this scope that depends on # the generated file, otherwise the original target won't have the # required dependencies in place to ensure correct build order. add_custom_target(${target}_${resourceName} DEPENDS ${generatedOutfile}) add_dependencies(${target} ${target}_${resourceName}) endif() if(rcc_BIG_RESOURCES) set(pass1OutputFile ${generatedOutfile}) set(generatedOutfile "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qrc_${resourceName}${CMAKE_CXX_OUTPUT_EXTENSION}") _qt_internal_add_rcc_pass2( RESOURCE_NAME ${resourceName} RCC_OPTIONS ${rccArgsAllPasses} OBJECT_LIB ${target}_${resourceName}_obj QRC_FILE ${generatedResourceFile} PASS1_OUTPUT_FILE ${pass1OutputFile} OUT_OBJECT_FILE ${generatedOutfile} ) get_target_property(type ${target} TYPE) if(type STREQUAL STATIC_LIBRARY) # Create a custom target to trigger the generation of ${generatedOutfile} set(pass2_target ${target}_${resourceName}_pass2) add_custom_target(${pass2_target} DEPENDS ${generatedOutfile}) add_dependencies(${target} ${pass2_target}) # Propagate the object files to the target. __qt_internal_propagate_object_files(${target} "${generatedOutfile}") else() target_sources(${target} PRIVATE ${generatedOutfile}) endif() else() __qt_propagate_generated_resource(${target} ${resourceName} "${generatedOutfile}" output_targets) endif() set_property(TARGET ${target} APPEND PROPERTY _qt_generated_qrc_files "${generatedResourceFile}") if (rcc_OUTPUT_TARGETS) set(${rcc_OUTPUT_TARGETS} "${output_targets}" PARENT_SCOPE) endif() endfunction() macro(_qt_internal_get_add_plugin_keywords option_args single_args multi_args) set(${option_args} STATIC SHARED __QT_INTERNAL_NO_PROPAGATE_PLUGIN_INITIALIZER ) set(${single_args} PLUGIN_TYPE # Internal use only, may be changed or removed CLASS_NAME OUTPUT_NAME # Internal use only, may be changed or removed OUTPUT_TARGETS ) set(${multi_args}) endmacro() function(qt6_add_plugin target) _qt_internal_get_add_plugin_keywords(opt_args single_args multi_args) list(APPEND opt_args MANUAL_FINALIZATION) cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}") if(arg_STATIC AND arg_SHARED) message(FATAL_ERROR "Both STATIC and SHARED options were given. Only one of the two should be used." ) endif() # Explicit option takes priority over the computed default. if(arg_STATIC) set(create_static_plugin TRUE) elseif(arg_SHARED) set(create_static_plugin FALSE) else() # If no explicit STATIC/SHARED option is set, default to the flavor of the Qt build. if(QT6_IS_SHARED_LIBS_BUILD) set(create_static_plugin FALSE) else() set(create_static_plugin TRUE) endif() endif() # The default of _qt_internal_add_library creates SHARED in a shared Qt build, so we need to # be explicit about the MODULE. if(create_static_plugin) set(type_to_create STATIC) else() set(type_to_create MODULE) endif() _qt_internal_add_library(${target} ${type_to_create} ${arg_UNPARSED_ARGUMENTS}) set_property(TARGET ${target} PROPERTY _qt_expects_finalization TRUE) get_target_property(target_type "${target}" TYPE) if (target_type STREQUAL "STATIC_LIBRARY") target_compile_definitions(${target} PRIVATE QT_STATICPLUGIN) endif() set(output_name ${target}) if (arg_OUTPUT_NAME) set(output_name ${arg_OUTPUT_NAME}) endif() set_property(TARGET "${target}" PROPERTY OUTPUT_NAME "${output_name}") if (ANDROID) set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_NAME "plugins_${arg_PLUGIN_TYPE}_${output_name}" ) endif() # Derive the class name from the target name if it's not explicitly specified. set(plugin_class_name "") if (NOT "${arg_PLUGIN_TYPE}" STREQUAL "qml_plugin") if (NOT arg_CLASS_NAME) set(plugin_class_name "${target}") else() set(plugin_class_name "${arg_CLASS_NAME}") endif() else() # Make sure to set any passed-in class name for qml plugins as well, because it's used for # building the qml plugin foo_init object libraries. if(arg_CLASS_NAME) set(plugin_class_name "${arg_CLASS_NAME}") else() message(FATAL_ERROR "Qml plugin target has no CLASS_NAME specified: '${target}'") endif() endif() set_target_properties(${target} PROPERTIES QT_PLUGIN_CLASS_NAME "${plugin_class_name}") # Create a plugin initializer object library for static plugins. # It contains a Q_IMPORT_PLUGIN(QT_PLUGIN_CLASS_NAME) call. # Project targets will automatically link to the plugin initializer whenever they link to the # plugin target. # The plugin init target name is stored in OUTPUT_TARGETS, so projects may install them. # Qml plugin inits are handled in Qt6QmlMacros. if(NOT "${arg_PLUGIN_TYPE}" STREQUAL "qml_plugin" AND target_type STREQUAL "STATIC_LIBRARY") __qt_internal_add_static_plugin_init_object_library("${target}" plugin_init_target) if(arg_OUTPUT_TARGETS) set(${arg_OUTPUT_TARGETS} ${plugin_init_target} PARENT_SCOPE) endif() # We don't automatically propagate the plugin init library for Qt provided plugins, because # there are 2 other code paths that take care of that, one involving finalizers and the # other regular usage requirements. if(NOT arg___QT_INTERNAL_NO_PROPAGATE_PLUGIN_INITIALIZER) __qt_internal_propagate_object_library("${target}" "${plugin_init_target}") endif() else() if(arg_OUTPUT_TARGETS) set(${arg_OUTPUT_TARGETS} "" PARENT_SCOPE) endif() endif() target_compile_definitions(${target} PRIVATE QT_PLUGIN QT_DEPRECATED_WARNINGS ) if(target_type STREQUAL "MODULE_LIBRARY") if(NOT TARGET qt_internal_plugins) add_custom_target(qt_internal_plugins) _qt_internal_assign_to_internal_targets_folder(qt_internal_plugins) endif() add_dependencies(qt_internal_plugins ${target}) endif() if(arg_MANUAL_FINALIZATION) # Caller says they will call qt6_finalize_target() themselves later return() endif() # Defer the finalization if we can. When the caller's project requires # CMake 3.19 or later, this makes the calls to this function concise while # still allowing target property modification before finalization. if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19) # Need to wrap in an EVAL CODE or else ${target} won't be evaluated # due to special behavior of cmake_language() argument handling cmake_language(EVAL CODE "cmake_language(DEFER CALL qt6_finalize_target ${target})") else() set_target_properties("${target}" PROPERTIES _qt_is_immediately_finalized TRUE) qt6_finalize_target("${target}") endif() endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) function(qt_add_plugin) qt6_add_plugin(${ARGV}) cmake_parse_arguments(PARSE_ARGV 1 arg "" "OUTPUT_TARGETS" "") if(arg_OUTPUT_TARGETS) set(${arg_OUTPUT_TARGETS} ${${arg_OUTPUT_TARGETS}} PARENT_SCOPE) endif() endfunction() endif() # Creates a library by forwarding arguments to add_library, applies some Qt naming file name naming # conventions and ensures the execution of Qt specific finalizers. function(qt6_add_library target) cmake_parse_arguments(PARSE_ARGV 1 arg "MANUAL_FINALIZATION" "" "") _qt_internal_add_library("${target}" ${arg_UNPARSED_ARGUMENTS}) set_property(TARGET ${target} PROPERTY _qt_expects_finalization TRUE) if(arg_MANUAL_FINALIZATION) # Caller says they will call qt6_finalize_target() themselves later return() endif() # Defer the finalization if we can. When the caller's project requires # CMake 3.19 or later, this makes the calls to this function concise while # still allowing target property modification before finalization. if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19) # Need to wrap in an EVAL CODE or else ${target} won't be evaluated # due to special behavior of cmake_language() argument handling cmake_language(EVAL CODE "cmake_language(DEFER CALL qt6_finalize_target ${target})") else() set_target_properties("${target}" PROPERTIES _qt_is_immediately_finalized TRUE) qt6_finalize_target("${target}") endif() endfunction() # Creates a library target by forwarding the arguments to add_library. # # Applies some Qt specific behaviors: # - If no type option is specified, rather than defaulting to STATIC it defaults to STATIC or SHARED # depending on the Qt configuration. # - Applies Qt specific prefixes and suffixes to file names depending on platform. function(_qt_internal_add_library target) set(opt_args STATIC SHARED MODULE INTERFACE OBJECT ) set(single_args "") set(multi_args "") cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}") set(option_type_count 0) if(arg_STATIC) set(type_to_create STATIC) math(EXPR option_type_count "${option_type_count}+1") elseif(arg_SHARED) set(type_to_create SHARED) math(EXPR option_type_count "${option_type_count}+1") elseif(arg_MODULE) set(type_to_create MODULE) math(EXPR option_type_count "${option_type_count}+1") elseif(arg_INTERFACE) set(type_to_create INTERFACE) math(EXPR option_type_count "${option_type_count}+1") elseif(arg_OBJECT) set(type_to_create OBJECT) math(EXPR option_type_count "${option_type_count}+1") endif() if(option_type_count GREATER 1) message(FATAL_ERROR "Multiple type options were given. Only one should be used." ) endif() # If no explicit type option is set, default to the flavor of the Qt build. # This in contrast to CMake which defaults to STATIC. if(NOT arg_STATIC AND NOT arg_SHARED AND NOT arg_MODULE AND NOT arg_INTERFACE AND NOT arg_OBJECT) if(QT6_IS_SHARED_LIBS_BUILD) set(type_to_create SHARED) else() set(type_to_create STATIC) endif() endif() add_library(${target} ${type_to_create} ${arg_UNPARSED_ARGUMENTS}) _qt_internal_set_up_static_runtime_library(${target}) if(NOT type_to_create STREQUAL "INTERFACE" AND NOT type_to_create STREQUAL "OBJECT") _qt_internal_apply_win_prefix_and_suffix("${target}") endif() if(arg_MODULE AND APPLE) # CMake defaults to using .so extensions for loadable modules, aka plugins, # but Qt plugins are actually suffixed with .dylib. set_property(TARGET "${target}" PROPERTY SUFFIX ".dylib") endif() if(ANDROID) set_property(TARGET "${target}" PROPERTY _qt_android_apply_arch_suffix_called_from_qt_impl TRUE) qt6_android_apply_arch_suffix("${target}") endif() endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) function(qt_add_library) qt6_add_library(${ARGV}) endfunction() endif() # TODO: Remove once all repositories use qt_internal_add_example instead of add_subdirectory. macro(_qt_internal_override_example_install_dir_to_dot) # Set INSTALL_EXAMPLEDIR to ".". # This overrides the install destination of unclean Qt example projects to install directly # to CMAKE_INSTALL_PREFIX. if(QT_INTERNAL_SET_EXAMPLE_INSTALL_DIR_TO_DOT) set(INSTALL_EXAMPLEDIR ".") set(_qt_internal_example_dir_set_to_dot TRUE) endif() endmacro() function(qt6_allow_non_utf8_sources target) set_target_properties("${target}" PROPERTIES QT_NO_UTF8_SOURCE TRUE) endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) function(qt_allow_non_utf8_sources) qt6_allow_non_utf8_sources(${ARGV}) endfunction() endif() function(_qt_internal_apply_strict_cpp target) # Disable C, Obj-C and C++ GNU extensions aka no "-std=gnu++11". # Similar to mkspecs/features/default_post.prf's CONFIG += strict_cpp. # Allow opt-out via variable. if(NOT QT_ENABLE_CXX_EXTENSIONS) get_target_property(target_type "${target}" TYPE) if(NOT target_type STREQUAL "INTERFACE_LIBRARY") set_target_properties("${target}" PROPERTIES CXX_EXTENSIONS OFF C_EXTENSIONS OFF OBJC_EXTENSIONS OFF OBJCXX_EXTENSIONS OFF) endif() endif() endfunction() # Copies properties of the dependency to the target. # Arguments: # PROPERTIES list of properties to copy. If not specified the following properties are copied # by default: INCLUDE_DIRECTORIES SYSTEM_INCLUDE_DIRECTORIES COMPILE_DEFINITIONS # COMPILE_OPTIONS COMPILE_FEATURES # PRIVATE_ONLY copy only private properties (without INTERFACE analogues). Optional. # INTERFACE_ONLY copy only interface properties (without non-prefixed analogues). Optional. # Note: Not all properties have INTERFACE properties analogues. # See https://cmake.org/cmake/help/latest/prop_tgt/EXPORT_PROPERTIES.html for details. # # PRIVATE_ONLY and INTERFACE_ONLY in the same call are not allowed. Omit these options to copy # both sets. function(_qt_internal_copy_dependency_properties target dependency) cmake_parse_arguments(arg "INTERFACE_ONLY;PRIVATE_ONLY" "" "PROPERTIES" ${ARGN}) if(arg_PRIVATE_ONLY AND arg_INTERFACE_ONLY) message("Both PRIVATE_ONLY and INTERFACE_ONLY options are set.\ Please use _qt_internal_copy_dependency_properties without these options to copy a set of properties of both types." ) endif() if(arg_PROPERTIES) set(common_props_to_set ${arg_PROPERTIES}) else() set(common_props_to_set INCLUDE_DIRECTORIES SYSTEM_INCLUDE_DIRECTORIES COMPILE_DEFINITIONS COMPILE_OPTIONS COMPILE_FEATURES ) endif() set(props_to_set "") if(NOT arg_INTERFACE_ONLY) set(props_to_set ${common_props_to_set}) endif() if(NOT arg_PRIVATE_ONLY) list(TRANSFORM common_props_to_set PREPEND INTERFACE_ OUTPUT_VARIABLE interface_properties) list(APPEND props_to_set ${interface_properties}) endif() foreach(prop ${props_to_set}) set_property(TARGET "${target}" APPEND PROPERTY ${prop} "$" ) endforeach() endfunction() function(qt6_disable_unicode_defines target) set_target_properties(${target} PROPERTIES QT_NO_UNICODE_DEFINES TRUE) endfunction() # Finalizer function for the top-level user projects. # # This function is currently in Technical Preview. # Its signature and behavior might change. function(qt6_finalize_project) if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) message("qt6_finalize_project is called not in the top-level CMakeLists.txt.") endif() if(ANDROID) _qt_internal_collect_apk_dependencies() endif() endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) function(qt_finalize_project) if(QT_DEFAULT_MAJOR_VERSION EQUAL 6) qt6_finalize_project() else() message(FATAL_ERROR "qt_finalize_project() is only available in Qt 6.") endif() endfunction() function(qt_disable_unicode_defines) qt6_disable_unicode_defines(${ARGV}) endfunction() endif() function(_qt_internal_get_deploy_impl_dir var) set(${var} "${CMAKE_BINARY_DIR}/.qt" PARENT_SCOPE) endfunction() function(_qt_internal_add_deploy_support deploy_support_file) get_filename_component(deploy_support_file "${deploy_support_file}" REALPATH) set(target ${QT_CMAKE_EXPORT_NAMESPACE}::Core) get_target_property(aliased_target ${target} ALIASED_TARGET) if(aliased_target) set(target ${aliased_target}) endif() get_property(scripts TARGET ${target} PROPERTY _qt_deploy_support_files) if(NOT "${deploy_support_file}" IN_LIST scripts) set_property(TARGET ${target} APPEND PROPERTY _qt_deploy_support_files "${deploy_support_file}" ) endif() endfunction() # Sets up the commands for use at install/deploy time function(_qt_internal_setup_deploy_support) if(QT_SKIP_SETUP_DEPLOYMENT) return() endif() get_property(cmake_role GLOBAL PROPERTY CMAKE_ROLE) if(NOT cmake_role STREQUAL "PROJECT") return() endif() # Always set QT_DEPLOY_SUPPORT in the caller's scope, even if we've generated # the deploy support file in a previous call. The project may be calling # find_package() from sibling directories with separate variable scopes. _qt_internal_get_deploy_impl_dir(deploy_impl_dir) get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) if(is_multi_config) set(QT_DEPLOY_SUPPORT "${deploy_impl_dir}/QtDeploySupport-$.cmake") else() set(QT_DEPLOY_SUPPORT "${deploy_impl_dir}/QtDeploySupport.cmake") endif() set(QT_DEPLOY_SUPPORT "${QT_DEPLOY_SUPPORT}" PARENT_SCOPE) get_property(have_generated_file GLOBAL PROPERTY _qt_have_generated_deploy_support) if(have_generated_file) return() endif() set_property(GLOBAL PROPERTY _qt_have_generated_deploy_support TRUE) include(GNUInstallDirs) set(target ${QT_CMAKE_EXPORT_NAMESPACE}::Core) get_target_property(aliased_target ${target} ALIASED_TARGET) if(aliased_target) set(target ${aliased_target}) endif() # Make sure to look under the Qt bin dir with find_program, rather than randomly picking up # a deployqt tool in the system. # QT6_INSTALL_PREFIX is not set during Qt build, so add the hints conditionally. set(find_program_hints) if(QT6_INSTALL_PREFIX) set(find_program_hints HINTS ${QT6_INSTALL_PREFIX}/${QT6_INSTALL_BINS}) endif() # In the generator expression logic below, we need safe_target_file because # CMake evaluates expressions in both the TRUE and FALSE branches of $. # We still need a target to give to $ when we have no deploy # tool, so we cannot use something like $ directly. if(APPLE AND NOT IOS) find_program(MACDEPLOYQT_EXECUTABLE macdeployqt ${find_program_hints}) set(fallback "$<$:${MACDEPLOYQT_EXECUTABLE}>") set(target_if_exists "$") set(have_deploy_tool "$") set(safe_target_file "$>") set(__QT_DEPLOY_TOOL "$") elseif(WIN32) find_program(WINDEPLOYQT_EXECUTABLE windeployqt ${find_program_hints}) set(fallback "$<$:${WINDEPLOYQT_EXECUTABLE}>") set(target_if_exists "$") set(have_deploy_tool "$") set(safe_target_file "$>") set(__QT_DEPLOY_TOOL "$") elseif(UNIX AND NOT APPLE AND NOT ANDROID AND NOT CMAKE_CROSSCOMPILING) set(__QT_DEPLOY_TOOL "GRD") else() # Android is handled as a build target, not via this install-based approach. # Therefore, we don't consider androiddeployqt here. set(__QT_DEPLOY_TOOL "") endif() _qt_internal_add_deploy_support("${CMAKE_CURRENT_LIST_DIR}/Qt6CoreDeploySupport.cmake") set(deploy_ignored_lib_dirs "") if(__QT_DEPLOY_TOOL STREQUAL "GRD" AND NOT "${QT6_INSTALL_PREFIX}" STREQUAL "") # Set up the directories we want to ignore when running file(GET_RUNTIME_DEPENDENCIES). # If the Qt prefix is the root of one of those directories, don't ignore that directory. # For example, if Qt's installation prefix is /usr, then we don't want to ignore /usr/lib. foreach(link_dir IN LISTS CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES) file(RELATIVE_PATH relative_dir "${QT6_INSTALL_PREFIX}" "${link_dir}") if(relative_dir STREQUAL "") # The Qt prefix is exactly ${link_dir}. continue() endif() if(IS_ABSOLUTE "${relative_dir}" OR relative_dir MATCHES "^\\.\\./") # The Qt prefix is outside of ${link_dir}. list(APPEND deploy_ignored_lib_dirs "${link_dir}") endif() endforeach() endif() # Check whether we will have to adjust the RPATH of plugins. if("${QT_DEPLOY_FORCE_ADJUST_RPATHS}" STREQUAL "") set(must_adjust_plugins_rpath "") if(NOT CMAKE_SYSTEM_NAME STREQUAL "Windows" AND NOT CMAKE_INSTALL_LIBDIR STREQUAL QT6_INSTALL_LIBS) set(must_adjust_plugins_rpath ON) endif() else() set(must_adjust_plugins_rpath "${QT_DEPLOY_FORCE_ADJUST_RPATHS}") endif() # Find the patchelf executable if necessary. if(must_adjust_plugins_rpath) if(CMAKE_VERSION VERSION_LESS "3.21") set(QT_DEPLOY_USE_PATCHELF ON) endif() if(QT_DEPLOY_USE_PATCHELF) find_program(QT_DEPLOY_PATCHELF_EXECUTABLE patchelf) if(NOT QT_DEPLOY_PATCHELF_EXECUTABLE) set(QT_DEPLOY_PATCHELF_EXECUTABLE "patchelf") message(WARNING "The patchelf executable could not be located. " "To use Qt's CMake deployment API, install patchelf or upgrade CMake to 3.21 " "or newer.") endif() endif() endif() file(GENERATE OUTPUT "${QT_DEPLOY_SUPPORT}" CONTENT "cmake_minimum_required(VERSION 3.16...3.21) # These are part of the public API. Projects should use them to provide a # consistent set of prefix-relative destinations. if(NOT QT_DEPLOY_BIN_DIR) set(QT_DEPLOY_BIN_DIR \"${CMAKE_INSTALL_BINDIR}\") endif() if(NOT QT_DEPLOY_LIB_DIR) set(QT_DEPLOY_LIB_DIR \"${CMAKE_INSTALL_LIBDIR}\") endif() if(NOT QT_DEPLOY_PLUGINS_DIR) set(QT_DEPLOY_PLUGINS_DIR \"plugins\") endif() if(NOT QT_DEPLOY_QML_DIR) set(QT_DEPLOY_QML_DIR \"qml\") endif() if(NOT QT_DEPLOY_TRANSLATIONS_DIR) set(QT_DEPLOY_TRANSLATIONS_DIR \"translations\") endif() if(NOT QT_DEPLOY_PREFIX) set(QT_DEPLOY_PREFIX \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}\") endif() if(QT_DEPLOY_PREFIX STREQUAL \"\") set(QT_DEPLOY_PREFIX .) endif() if(NOT QT_DEPLOY_IGNORED_LIB_DIRS) set(QT_DEPLOY_IGNORED_LIB_DIRS \"${deploy_ignored_lib_dirs}\") endif() # These are internal implementation details. They may be removed at any time. set(__QT_DEPLOY_SYSTEM_NAME \"${CMAKE_SYSTEM_NAME}\") set(__QT_DEPLOY_IS_SHARED_LIBS_BUILD \"${QT6_IS_SHARED_LIBS_BUILD}\") set(__QT_DEPLOY_TOOL \"${__QT_DEPLOY_TOOL}\") set(__QT_DEPLOY_IMPL_DIR \"${deploy_impl_dir}\") set(__QT_DEPLOY_VERBOSE \"${QT_ENABLE_VERBOSE_DEPLOYMENT}\") set(__QT_CMAKE_EXPORT_NAMESPACE \"${QT_CMAKE_EXPORT_NAMESPACE}\") set(__QT_DEPLOY_GENERATOR_IS_MULTI_CONFIG \"${is_multi_config}\") set(__QT_DEPLOY_ACTIVE_CONFIG \"$\") set(__QT_NO_CREATE_VERSIONLESS_FUNCTIONS \"${QT_NO_CREATE_VERSIONLESS_FUNCTIONS}\") set(__QT_DEFAULT_MAJOR_VERSION \"${QT_DEFAULT_MAJOR_VERSION}\") set(__QT_DEPLOY_QT_ADDITIONAL_PACKAGES_PREFIX_PATH \"${QT_ADDITIONAL_PACKAGES_PREFIX_PATH}\") set(__QT_DEPLOY_QT_INSTALL_PREFIX \"${QT6_INSTALL_PREFIX}\") set(__QT_DEPLOY_QT_INSTALL_BINS \"${QT6_INSTALL_BINS}\") set(__QT_DEPLOY_QT_INSTALL_PLUGINS \"${QT6_INSTALL_PLUGINS}\") set(__QT_DEPLOY_QT_INSTALL_TRANSLATIONS \"${QT6_INSTALL_TRANSLATIONS}\") set(__QT_DEPLOY_PLUGINS \"\") set(__QT_DEPLOY_MUST_ADJUST_PLUGINS_RPATH \"${must_adjust_plugins_rpath}\") set(__QT_DEPLOY_USE_PATCHELF \"${QT_DEPLOY_USE_PATCHELF}\") set(__QT_DEPLOY_PATCHELF_EXECUTABLE \"${QT_DEPLOY_PATCHELF_EXECUTABLE}\") # Define the CMake commands to be made available during deployment. set(__qt_deploy_support_files \"$,\" \">\" ) foreach(__qt_deploy_support_file IN LISTS __qt_deploy_support_files) include(\"\${__qt_deploy_support_file}\") endforeach() unset(__qt_deploy_support_file) unset(__qt_deploy_support_files) ") endfunction() # We basically mirror CMake's policy setup # A policy can be set to OLD, set to NEW or unset # unset is the default state # function(qt6_policy mode policy behaviorOrVariable) # When building Qt, tests and examples might expect a policy to be known, but they won't be # known depending on which scope or when a find_package(Module) with the respective policy # is called. Check the global list of known policies to accommodate that. if(QT_BUILDING_QT AND NOT DEFINED QT_KNOWN_POLICY_${policy}) get_property(global_known_policies GLOBAL PROPERTY _qt_global_known_policies) if(policy IN_LIST global_known_policies) set(QT_KNOWN_POLICY_${policy} TRUE) endif() endif() if (NOT DEFINED QT_KNOWN_POLICY_${policy}) message(FATAL_ERROR "${policy} is not a known Qt policy. Did you include the necessary Qt module?" ) endif() if (${mode} STREQUAL "SET") set(behavior ${behaviorOrVariable}) if (${behavior} STREQUAL "NEW" OR ${behavior} STREQUAL "OLD") set(__QT_INTERNAL_POLICY_${policy} ${behavior} PARENT_SCOPE) else() message(FATAL_ERROR "Qt policies must be either set to NEW or OLD, but got ${behavior}") endif() else(${mode} STREQUAL "GET") set(variable "${behaviorOrVariable}") set("${variable}" "${__QT_INTERNAL_POLICY_${policy}}" PARENT_SCOPE) endif() endfunction() # Internal helper function; can be used in any module before doing a policy check function(__qt_internal_setup_policy policy sinceversion policyexplanation) if(DEFINED __QT_INTERNAL_POLICY_${policy}) if (__QT_INTERNAL_POLICY_${policy} STREQUAL "OLD") # policy is explicitly disabled message(DEPRECATION "Qt policy ${policy} is set to OLD. " "Support for the old behavior will be removed in a future major version of Qt." ) endif() #else: policy is already enabled, nothing to do elseif (${sinceversion} VERSION_LESS_EQUAL __qt_policy_check_version) # we cannot use the public function here as we want to set it in parent scope set(__QT_INTERNAL_POLICY_${policy} "NEW" PARENT_SCOPE) elseif(NOT "${QT_NO_SHOW_OLD_POLICY_WARNINGS}") message(AUTHOR_WARNING "Qt policy ${policy} is not set: " "${policyexplanation} " "Use the qt_policy command to set the policy and suppress this warning.\n" ) endif() endfunction() # Note this needs to be a macro because it sets variables intended for the # calling scope. macro(qt6_standard_project_setup) # A parent project might want to prevent child projects pulled in with # add_subdirectory() from changing the parent's preferred arrangement. # They can set this variable to true to effectively disable this function. if(NOT QT_NO_STANDARD_PROJECT_SETUP) set(__qt_sps_args_option) set(__qt_sps_args_single REQUIRES SUPPORTS_UP_TO ) set(__qt_sps_args_multi) cmake_parse_arguments(__qt_sps_arg "${__qt_sps_args_option}" "${__qt_sps_args_single}" "${__qt_sps_args_multi}" ${ARGN} ) if(__qt_sps_arg_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unexpected arguments: ${arg_UNPARSED_ARGUMENTS}") endif() # Set the Qt CMake policy based on the requested version(s) set(__qt_policy_check_version "6.0.0") if(Qt6_VERSION_MAJOR) set(__qt_current_version "${Qt6_VERSION_MAJOR}.${Qt6_VERSION_MINOR}.${Qt6_VERSION_PATCH}") elseif(QT_BUILDING_QT) set(__qt_current_version "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") else() message(FATAL_ERROR "Can not determine Qt version.") endif() if(__qt_sps_arg_REQUIRES) if("${__qt_current_version}" VERSION_LESS "${__qt_sps_arg_REQUIRES}") message(FATAL_ERROR "Project required a Qt minimum version of ${__qt_sps_arg_REQUIRES}, " "but current version is only ${__qt_current_version}.") endif() set(__qt_policy_check_version "${__qt_sps_arg_REQUIRES}") endif() if(__qt_sps_arg_SUPPORTS_UP_TO) if(__qt_sps_arg_REQUIRES) if(${__qt_sps_arg_SUPPORTS_UP_TO} VERSION_LESS ${__qt_sps_arg_REQUIRES}) message(FATAL_ERROR "SUPPORTS_UP_TO must be larger than or equal to REQUIRES.") endif() set(__qt_policy_check_version "${__qt_sps_arg_SUPPORTS_UP_TO}") else() message(FATAL_ERROR "Please specify the REQUIRES as well.") endif() endif() # All changes below this point should not result in a change to an # existing value, except for CMAKE_INSTALL_RPATH which may append new # values (but no duplicates). # Use standard install locations, provided by GNUInstallDirs. All # platforms should have this included so that we know the # CMAKE_INSTALL_xxxDIR variables will be set. include(GNUInstallDirs) if(WIN32) # Windows has no RPATH support, so we need all non-plugin DLLs in # the same directory as application executables if we want to be # able to run them without having to augment the PATH environment # variable. Don't discard an existing value in case the project has # already set this to somewhere else. Our setting is somewhat # opinionated, so make it easy for projects to choose something else. if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) endif() elseif(NOT APPLE) # Apart from Windows and Apple, most other platforms support RPATH # and $ORIGIN. Make executables and non-static libraries use an # install RPATH that allows them to find library dependencies if the # project installs things to the directories defined by the # CMAKE_INSTALL_xxxDIR variables (which is what CMake's defaults # are based on). file(RELATIVE_PATH __qt_relDir ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR} ) list(APPEND CMAKE_INSTALL_RPATH $ORIGIN $ORIGIN/${__qt_relDir}) list(REMOVE_DUPLICATES CMAKE_INSTALL_RPATH) unset(__qt_reldir) endif() # Turn these on by default, unless they are already set. Projects can # always turn off any they really don't want after we return. foreach(auto_set IN ITEMS MOC UIC) if(NOT DEFINED CMAKE_AUTO${auto_set}) set(CMAKE_AUTO${auto_set} TRUE) endif() endforeach() # Enable folder support for IDEs. CMake >= 3.26 enables USE_FOLDERS by default but this is # guarded by CMake policy CMP0143. get_property(__qt_use_folders GLOBAL PROPERTY USE_FOLDERS) if(__qt_use_folders OR "${__qt_use_folders}" STREQUAL "") set_property(GLOBAL PROPERTY USE_FOLDERS ON) get_property(__qt_qt_targets_folder GLOBAL PROPERTY QT_TARGETS_FOLDER) if("${__qt_qt_targets_folder}" STREQUAL "") set(__qt_qt_targets_folder QtInternalTargets) set_property(GLOBAL PROPERTY QT_TARGETS_FOLDER ${__qt_qt_targets_folder}) endif() get_property(__qt_autogen_targets_folder GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER) if("${__qt_autogen_targets_folder}" STREQUAL "") set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER ${__qt_qt_targets_folder}) endif() endif() endif() endmacro() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) macro(qt_standard_project_setup) qt6_standard_project_setup(${ARGV}) endmacro() macro(qt_policy) qt6_policy(${ARGV}) endmacro() endif() function(qt6_generate_deploy_script) set(no_value_options "") set(single_value_options CONTENT OUTPUT_SCRIPT NAME TARGET # TODO: For backward compatibility / transitional use only, # remove at some point FILENAME_VARIABLE ) set(multi_value_options "") cmake_parse_arguments(PARSE_ARGV 0 arg "${no_value_options}" "${single_value_options}" "${multi_value_options}" ) if(arg_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unexpected arguments: ${arg_UNPARSED_ARGUMENTS}") endif() # TODO: Remove when FILENAME_VARIABLE is fully removed # Handle the slow deprecation of FILENAME_VARIABLE if(arg_FILENAME_VARIABLE) if(arg_OUTPUT_SCRIPT AND NOT arg_FILENAME_VARIABLE STREQUAL arg_OUTPUT_SCRIPT) message(FATAL_ERROR "Both FILENAME_VARIABLE and OUTPUT_SCRIPT were given and were different. " "Only one of the two should be used." ) endif() message(AUTHOR_WARNING "The FILENAME_VARIABLE keyword is deprecated and will be removed soon. Please use OUTPUT_SCRIPT instead.") set(arg_OUTPUT_SCRIPT "${arg_FILENAME_VARIABLE}") unset(arg_FILENAME_VARIABLE) endif() if(NOT arg_OUTPUT_SCRIPT) message(FATAL_ERROR "OUTPUT_SCRIPT must be specified") endif() if("${arg_CONTENT}" STREQUAL "") message(FATAL_ERROR "CONTENT must be specified") endif() # Check whether manual finalization is needed. if(CMAKE_VERSION VERSION_LESS "3.19") get_target_property(is_immediately_finalized ${arg_TARGET} _qt_is_immediately_finalized) if(is_immediately_finalized) message(WARNING "Deployment of plugins for target '${arg_TARGET}' will not work. " "Either, upgrade CMake to version 3.19 or newer, or call " "qt_finalize_target(${arg_TARGET}) after generating the deployment script." ) endif() endif() # Mark the target as "to be deployed". set_property(TARGET ${arg_TARGET} PROPERTY _qt_marked_for_deployment ON) # If the target already was finalized, maybe because it was defined in a subdirectory, generate # the plugin deployment information here. get_target_property(is_finalized "${arg_TARGET}" _qt_is_finalized) if(is_finalized) __qt_internal_generate_plugin_deployment_info(${arg_TARGET}) endif() # Create a file name that will be unique for this target and the combination # of arguments passed to this command. This allows the project to call us # multiple times with different arguments for the same target (e.g. to # create deployment scripts for different scenarios). set(file_base_name "custom") if(NOT "${arg_NAME}" STREQUAL "") set(file_base_name "${arg_NAME}") elseif(NOT "${arg_TARGET}" STREQUAL "") set(file_base_name "${arg_TARGET}") endif() string(MAKE_C_IDENTIFIER "${file_base_name}" target_id) string(SHA1 args_hash "${ARGV}") string(SUBSTRING "${args_hash}" 0 10 short_hash) _qt_internal_get_deploy_impl_dir(deploy_impl_dir) set(deploy_script "${deploy_impl_dir}/deploy_${target_id}_${short_hash}") get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) if(is_multi_config) set(config_infix "-$") else() set(config_infix "") endif() string(APPEND deploy_script "${config_infix}.cmake") set(${arg_OUTPUT_SCRIPT} "${deploy_script}" PARENT_SCOPE) set(boiler_plate "include(${QT_DEPLOY_SUPPORT}) include(\"\${CMAKE_CURRENT_LIST_DIR}/${arg_TARGET}-plugins${config_infix}.cmake\" OPTIONAL) set(__QT_DEPLOY_ALL_MODULES_FOUND_VIA_FIND_PACKAGE \"${QT_ALL_MODULES_FOUND_VIA_FIND_PACKAGE}\") ") list(TRANSFORM arg_CONTENT REPLACE "\\$" "\$") file(GENERATE OUTPUT ${deploy_script} CONTENT "${boiler_plate}${arg_CONTENT}") endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) macro(qt_generate_deploy_script) if(QT_DEFAULT_MAJOR_VERSION EQUAL 6) qt6_generate_deploy_script(${ARGV}) else() message(FATAL_ERROR "qt_generate_deploy_script() is only available in Qt 6.") endif() endmacro() endif() function(qt6_generate_deploy_app_script) # We use a TARGET keyword option instead of taking the target as the first # positional argument. This is to keep open the possibility of deploying # an app for which we don't have a target (e.g. an application from a # third party project that the caller may want to include in their own # package). We would add an EXECUTABLE keyword for that, which would be # mutually exclusive with the TARGET keyword. set(no_value_options NO_TRANSLATIONS NO_COMPILER_RUNTIME NO_UNSUPPORTED_PLATFORM_ERROR ) set(single_value_options TARGET OUTPUT_SCRIPT # TODO: For backward compatibility / transitional use only, # remove at some point FILENAME_VARIABLE ) set(qt_deploy_runtime_dependencies_options # These options are forwarded as is to qt_deploy_runtime_dependencies. PRE_INCLUDE_REGEXES PRE_EXCLUDE_REGEXES POST_INCLUDE_REGEXES POST_EXCLUDE_REGEXES POST_INCLUDE_FILES POST_EXCLUDE_FILES ) set(multi_value_options ${qt_deploy_runtime_dependencies_options} ) cmake_parse_arguments(PARSE_ARGV 0 arg "${no_value_options}" "${single_value_options}" "${multi_value_options}" ) if(arg_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unexpected arguments: ${arg_UNPARSED_ARGUMENTS}") endif() if(NOT arg_TARGET) message(FATAL_ERROR "TARGET must be specified") endif() # TODO: Remove when FILENAME_VARIABLE is fully removed # Handle the slow deprecation of FILENAME_VARIABLE if(arg_FILENAME_VARIABLE) if(arg_OUTPUT_SCRIPT AND NOT arg_FILENAME_VARIABLE STREQUAL arg_OUTPUT_SCRIPT) message(FATAL_ERROR "Both FILENAME_VARIABLE and OUTPUT_SCRIPT were given and were different. " "Only one of the two should be used." ) endif() message(AUTHOR_WARNING "The FILENAME_VARIABLE keyword is deprecated and will be removed soon. Please use OUTPUT_SCRIPT instead.") set(arg_OUTPUT_SCRIPT "${arg_FILENAME_VARIABLE}") unset(arg_FILENAME_VARIABLE) endif() if(NOT arg_OUTPUT_SCRIPT) message(FATAL_ERROR "OUTPUT_SCRIPT must be specified") endif() if(QT6_IS_SHARED_LIBS_BUILD) set(qt_build_type_string "shared Qt libs") else() set(qt_build_type_string "static Qt libs") endif() if(CMAKE_CROSSCOMPILING) string(APPEND qt_build_type_string ", cross-compiled") endif() set(generate_args TARGET ${arg_TARGET} OUTPUT_SCRIPT deploy_script ) set(common_deploy_args "") if(arg_NO_TRANSLATIONS) string(APPEND common_deploy_args " NO_TRANSLATIONS\n") endif() if(arg_NO_COMPILER_RUNTIME) string(APPEND common_deploy_args " NO_COMPILER_RUNTIME\n") endif() # Forward the arguments that are exactly the same for qt_deploy_runtime_dependencies. foreach(var IN LISTS qt_deploy_runtime_dependencies_options) if(NOT "${arg_${var}}" STREQUAL "") list(APPEND common_deploy_args ${var} ${arg_${var}}) endif() endforeach() if(APPLE AND NOT IOS AND QT6_IS_SHARED_LIBS_BUILD) # TODO: Handle non-bundle applications if possible. get_target_property(is_bundle ${arg_TARGET} MACOSX_BUNDLE) if(NOT is_bundle) message(FATAL_ERROR "Executable targets have to be app bundles to use this command " "on Apple platforms." ) endif() qt6_generate_deploy_script(${generate_args} CONTENT " qt6_deploy_runtime_dependencies( EXECUTABLE $.app ${common_deploy_args}) ") elseif(WIN32 AND QT6_IS_SHARED_LIBS_BUILD) qt6_generate_deploy_script(${generate_args} CONTENT " qt6_deploy_runtime_dependencies( EXECUTABLE $ GENERATE_QT_CONF ${common_deploy_args}) ") elseif(UNIX AND NOT APPLE AND NOT ANDROID AND QT6_IS_SHARED_LIBS_BUILD AND NOT CMAKE_CROSSCOMPILING) qt6_generate_deploy_script(${generate_args} CONTENT " qt6_deploy_runtime_dependencies( EXECUTABLE $ GENERATE_QT_CONF ${common_deploy_args}) ") elseif(NOT arg_NO_UNSUPPORTED_PLATFORM_ERROR AND NOT QT_INTERNAL_NO_UNSUPPORTED_PLATFORM_ERROR) # Currently we don't deploy runtime dependencies if cross-compiling or using a static Qt. # We also don't do it if targeting Linux, but we could provide an option to do # so if we had a deploy tool or purely CMake-based deploy implementation. # Error out by default unless the project opted out of the error. # This provides us a migration path in the future without breaking compatibility promises. message(FATAL_ERROR "Support for installing runtime dependencies is not implemented for " "this target platform (${CMAKE_SYSTEM_NAME}, ${qt_build_type_string})." ) else() qt6_generate_deploy_script(${generate_args} CONTENT " _qt_internal_show_skip_runtime_deploy_message(\"${qt_build_type_string}\") ") endif() set(${arg_OUTPUT_SCRIPT} "${deploy_script}" PARENT_SCOPE) endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) macro(qt_generate_deploy_app_script) qt6_generate_deploy_app_script(${ARGV}) endmacro() endif()