diff options
Diffstat (limited to 'src/corelib/Qt6CoreMacros.cmake')
-rw-r--r-- | src/corelib/Qt6CoreMacros.cmake | 959 |
1 files changed, 847 insertions, 112 deletions
diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index 9456687c31..057cb506f9 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -93,24 +93,43 @@ function(_qt_internal_create_moc_command infile outfile moc_flags moc_options set(extra_output_files "${outfile}.json") set(${out_json_file} "${extra_output_files}" PARENT_SCOPE) endif() - string (REPLACE ";" "\n" _moc_parameters "${_moc_parameters}") if(moc_target) set(_moc_parameters_file ${_moc_parameters_file}$<$<BOOL:$<CONFIG>>:_$<CONFIG>>) set(targetincludes "$<TARGET_PROPERTY:${moc_target},INCLUDE_DIRECTORIES>") set(targetdefines "$<TARGET_PROPERTY:${moc_target},COMPILE_DEFINITIONS>") - set(targetincludes "$<$<BOOL:${targetincludes}>:-I$<JOIN:$<REMOVE_DUPLICATES:${targetincludes}>,\n-I>\n>") - set(targetdefines "$<$<BOOL:${targetdefines}>:-D$<JOIN:$<REMOVE_DUPLICATES:${targetdefines}>,\n-D>\n>") + set(targetincludes "$<$<BOOL:${targetincludes}>:-I$<JOIN:${targetincludes},;-I>>") + set(targetdefines "$<$<BOOL:${targetdefines}>:-D$<JOIN:${targetdefines},;-D>>") + set(_moc_parameters_list_without_genex) + set(_moc_parameters_list_with_genex) + foreach(_moc_parameter ${_moc_parameters}) + if(_moc_parameter MATCHES "\\\$<") + list(APPEND _moc_parameters_list_with_genex ${_moc_parameter}) + else() + list(APPEND _moc_parameters_list_without_genex ${_moc_parameter}) + endif() + endforeach() + + string(REPLACE ">" "$<ANGLE-R>" _moc_escaped_parameters "${_moc_parameters_list_without_genex}") + string(REPLACE "," "$<COMMA>" _moc_escaped_parameters "${_moc_escaped_parameters}") + string(REPLACE ";" "$<SEMICOLON>" _moc_escaped_parameters "${_moc_escaped_parameters}") + set(concatenated "$<$<BOOL:${targetincludes}>:${targetincludes};>$<$<BOOL:${targetdefines}>:${targetdefines};>$<$<BOOL:${_moc_escaped_parameters}>:${_moc_escaped_parameters};>") + + list(APPEND concatenated ${_moc_parameters_list_with_genex}) + set(concatenated "$<FILTER:$<REMOVE_DUPLICATES:${concatenated}>,EXCLUDE,^-[DI]$>") + set(concatenated "$<JOIN:${concatenated},\n>") file (GENERATE OUTPUT ${_moc_parameters_file} - CONTENT "${targetdefines}${targetincludes}${_moc_parameters}\n" + 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() @@ -121,8 +140,12 @@ function(_qt_internal_create_moc_command infile outfile moc_flags moc_options ${_moc_working_dir} VERBATIM) set_source_files_properties(${infile} PROPERTIES SKIP_AUTOMOC ON) - set_source_files_properties(${outfile} PROPERTIES SKIP_AUTOMOC ON) - set_source_files_properties(${outfile} PROPERTIES SKIP_AUTOUIC 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 ) @@ -177,7 +200,49 @@ function(qt6_wrap_cpp outfiles ) foreach(it ${moc_files}) get_filename_component(it ${it} ABSOLUTE) - _qt_internal_make_output_file(${it} moc_ cpp outfile) + get_filename_component(it_ext ${it} EXT) + # remove the dot + string(SUBSTRING ${it_ext} 1 -1 it_ext) + set(HEADER_REGEX "(h|hh|h\\+\\+|hm|hpp|hxx|in|txx|inl)$") + + if(it_ext MATCHES "${HEADER_REGEX}") + _qt_internal_make_output_file("${it}" moc_ cpp outfile) + set(is_header_file TRUE) + else() + set(found_source_extension FALSE) + foreach(LANG C CXX OBJC OBJCXX CUDA) + list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${it_ext}" + index) + if(${index} GREATER -1) + set(found_extension TRUE) + break() + endif() + endforeach() + if(found_extension) + if(TARGET ${moc_target}) + _qt_internal_make_output_file(${it} "" moc outfile) + target_sources(${moc_target} PRIVATE "${outfile}") + target_include_directories("${moc_target}" PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}") + else() + if("${moc_target}" STREQUAL "") + string(JOIN "" err_msg + "qt6_wrap_cpp: TARGET parameter is empty. " + "Since the file ${it} is a source file, " + "the TARGET option must be specified.") + else() + string(JOIN "" err_msg + "qt6_wrap_cpp: TARGET \"${moc_target}\" " + "not found.") + endif() + message(FATAL_ERROR "${err_msg}") + endif() + else() + string(JOIN "" err_msg "qt6_wrap_cpp: Unknown file extension: " + "\"\.${it_ext}\".") + message(FATAL_ERROR "${err_msg}") + endif() + endif() set(out_json_file_var "") if(_WRAP_CPP___QT_INTERNAL_OUTPUT_MOC_JSON_FILES) @@ -192,7 +257,10 @@ function(qt6_wrap_cpp outfiles ) list(APPEND metatypes_json_list "${${out_json_file_var}}") endif() endforeach() - set(${outfiles} ${${outfiles}} PARENT_SCOPE) + + if(is_header_file) + set(${outfiles} "${${outfiles}}" PARENT_SCOPE) + endif() if(metatypes_json_list) set(${_WRAP_CPP___QT_INTERNAL_OUTPUT_MOC_JSON_FILES} @@ -434,6 +502,9 @@ function(qt6_add_big_resources outfiles ) _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 @@ -546,6 +617,8 @@ set(_Qt6_COMPONENT_PATH "${CMAKE_CURRENT_LIST_DIR}/..") function(qt6_add_executable target) cmake_parse_arguments(PARSE_ARGV 1 arg "MANUAL_FINALIZATION" "" "") + _qt_internal_warn_about_example_add_subdirectory() + _qt_internal_create_executable("${target}" ${arg_UNPARSED_ARGUMENTS}) target_link_libraries("${target}" PRIVATE Qt6::Core) set_property(TARGET ${target} PROPERTY _qt_expects_finalization TRUE) @@ -568,6 +641,21 @@ function(qt6_add_executable target) endif() endfunction() +# Just like for qt_add_resources, we should disable zstd compression when cross-compiling to a +# target that doesn't support zstd decompression, even if the host tool supports it. +# Allow an opt out via a QT_NO_AUTORCC_ZSTD variable. +function(_qt_internal_disable_autorcc_zstd_when_not_supported target) + if(TARGET "${target}" + AND DEFINED QT_FEATURE_zstd + AND NOT QT_FEATURE_zstd + AND NOT QT_NO_AUTORCC_ZSTD) + get_target_property(target_type ${target} TYPE) + if(NOT target_type STREQUAL "INTERFACE_LIBRARY") + set_property(TARGET "${target}" APPEND PROPERTY AUTORCC_OPTIONS "--no-zstd") + endif() + endif() +endfunction() + function(_qt_internal_create_executable target) if(ANDROID) list(REMOVE_ITEM ARGN "WIN32" "MACOSX_BUNDLE") @@ -588,18 +676,11 @@ function(_qt_internal_create_executable target) add_executable("${target}" ${ARGN}) endif() + _qt_internal_disable_autorcc_zstd_when_not_supported("${target}") _qt_internal_set_up_static_runtime_library("${target}") endfunction() function(_qt_internal_finalize_executable target) - get_target_property(is_finalized "${target}" _qt_executable_is_finalized) - if(is_finalized) - message(AUTHOR_WARNING - "Tried to call qt6_finalize_target twice on executable: '${target}'. \ - Did you forget to specify MANUAL_FINALIZATION to qt6_add_executable?") - return() - endif() - # 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. @@ -647,13 +728,16 @@ function(_qt_internal_finalize_executable target) endif() if(EMSCRIPTEN) - _qt_internal_wasm_add_target_helpers("${target}") - _qt_internal_add_wasm_extra_exported_methods("${target}") + _qt_internal_finalize_wasm_app("${target}") endif() - if(IOS) - _qt_internal_finalize_ios_app("${target}") - elseif(APPLE) - _qt_internal_finalize_macos_app("${target}") + + if(APPLE) + if(NOT CMAKE_SYSTEM_NAME OR CMAKE_SYSTEM_NAME STREQUAL "Darwin") + # macOS + _qt_internal_finalize_macos_app("${target}") + else() + _qt_internal_finalize_uikit_app("${target}") + endif() endif() # For finalizer mode of plugin importing to work safely, we need to know the list of Qt @@ -666,8 +750,6 @@ function(_qt_internal_finalize_executable target) __qt_internal_apply_plugin_imports_finalizer_mode("${target}") __qt_internal_process_dependency_object_libraries("${target}") endif() - - set_target_properties(${target} PROPERTIES _qt_executable_is_finalized TRUE) endfunction() function(_cat IN_FILE OUT_FILE) @@ -697,6 +779,7 @@ function(_qt_internal_delay_finalization_until_after 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) @@ -722,13 +805,99 @@ function(qt6_finalize_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}) + _qt_internal_finalize_source_groups(${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() + + if(APPLE) + # Tell CMake to generate run-scheme for the executable when generating + # Xcode projects. This avoids Xcode auto-generating default schemes for + # all targets, which includes internal and build-only targets. + get_target_property(generate_scheme "${target}" XCODE_GENERATE_SCHEME) + if(generate_scheme MATCHES "-NOTFOUND" AND ( + target_type STREQUAL "EXECUTABLE" OR + target_type STREQUAL "SHARED_LIBRARY" OR + target_type STREQUAL "STATIC_LIBRARY" OR + target_type STREQUAL "MODULE_LIBRARY")) + set_property(TARGET "${target}" PROPERTY XCODE_GENERATE_SCHEME TRUE) + endif() + endif() + + if(target_type STREQUAL "SHARED_LIBRARY" OR + target_type STREQUAL "STATIC_LIBRARY" OR + target_type STREQUAL "MODULE_LIBRARY" OR + target_type STREQUAL "OBJECT_LIBRARY") + get_target_property(is_immediately_finalized "${target}" _qt_is_immediately_finalized) + get_target_property(uses_automoc ${target} AUTOMOC) + if(uses_automoc AND NOT is_immediately_finalized) + qt6_extract_metatypes(${target}) + endif() + endif() + + set_target_properties(${target} PROPERTIES _qt_is_finalized TRUE) +endfunction() + +function(_qt_internal_finalize_source_groups target) + if(NOT ("${CMAKE_GENERATOR}" STREQUAL "Xcode" + OR "${CMAKE_GENERATOR}" MATCHES "^Visual Studio")) + return() + endif() + + get_target_property(sources ${target} SOURCES) + if(NOT sources) + return() + endif() + + get_target_property(source_dir ${target} SOURCE_DIR) + get_target_property(binary_dir ${target} BINARY_DIR) + + get_property(generated_source_group GLOBAL PROPERTY AUTOGEN_SOURCE_GROUP) + if(NOT generated_source_group) + set(generated_source_group "Source Files/Generated") + endif() + + foreach(source IN LISTS sources) + string(GENEX_STRIP "${source}" source) + + if(IS_ABSOLUTE ${source}) + set(source_file_path "${source}") + else() + # Resolve absolute path. Can't use LOCATION, as that + # will error out if the file doesn't exist :( + get_filename_component(source_file_path "${source}" + ABSOLUTE BASE_DIR "${source_dir}") + if(NOT EXISTS ${source_file_path}) + # Likely generated file, will end up in build dir + get_filename_component(source_file_path "${source}" + ABSOLUTE BASE_DIR "${binary_dir}") + endif() + endif() + + # Include qml files in "Source Files". Can not be done via regex, + # due to https://gitlab.kitware.com/cmake/cmake/-/issues/25597 + if(${source_file_path} MATCHES "\\.qml$") + source_group("Source Files" FILES ${source_file_path}) + endif() + + get_source_file_property(is_generated "${source_file_path}" GENERATED) + if(${is_generated}) + source_group(${generated_source_group} FILES ${source_file_path}) + endif() + endforeach() endfunction() function(_qt_internal_darwin_permission_finalizer target) @@ -902,6 +1071,160 @@ function(_qt_internal_assign_to_internal_targets_folder target) 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(_qt_internal_should_install_metatypes target) + set(args_option + INTERNAL_INSTALL + ) + set(args_single + OUT_VAR + ) + set(args_multi + ) + + cmake_parse_arguments(arg + "${args_option}" + "${args_single}" + "${args_multi}" ${ARGN}) + + # 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) + + get_target_property(target_type ${target} TYPE) + if(NOT target_type STREQUAL "EXECUTABLE" AND arg_INTERNAL_INSTALL) + set(should_install TRUE) + endif() + set(${arg_OUT_VAR} "${should_install}" PARENT_SCOPE) +endfunction() + +function(_qt_internal_get_metatypes_install_dir internal_install_dir arch_data_dir out_var) + # Automatically fill default install args when not specified. + if(NOT 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(arch_data_dir) + set(install_dir "${arch_data_dir}/metatypes") + else() + set(install_dir "lib/metatypes") + endif() + else() + set(install_dir "${internal_install_dir}") + endif() + set(${out_var} "${install_dir}" PARENT_SCOPE) +endfunction() + +# Propagates the build time metatypes file via INTERFACE_SOURCES (using $<BUILD_INTERFACE>) +# and saves the path and file name in properties, so that they can be queryied in the qml api +# implementation for the purpose of duplicating a qml module backing library's metatypes in its +# associated plugin. This is required for qmltyperegistrar to get the full set of foreign types +# when projects link to the plugin and not the backing library. +function(_qt_internal_assign_build_metatypes_files_and_properties 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 + ) + set(args_single + METATYPES_FILE_NAME + METATYPES_FILE_PATH + ) + set(args_multi + ) + + cmake_parse_arguments(arg + "${args_option}" + "${args_single}" + "${args_multi}" ${ARGN}) + + if(NOT arg_METATYPES_FILE_NAME) + message(FATAL_ERROR "METATYPES_FILE_NAME must be specified") + endif() + + if(NOT arg_METATYPES_FILE_PATH) + message(FATAL_ERROR "METATYPES_FILE_PATH must be specified") + endif() + + set(metatypes_file_name "${arg_METATYPES_FILE_NAME}") + set(metatypes_file_path "${arg_METATYPES_FILE_PATH}") + + # Set up consumption of files via INTERFACE_SOURCES. + set(consumes_metatypes "$<BOOL:$<TARGET_PROPERTY:QT_CONSUMES_METATYPES>>") + set(metatypes_file_genex_build + "$<BUILD_INTERFACE:$<${consumes_metatypes}:${metatypes_file_path}>>" + ) + target_sources(${target} INTERFACE ${metatypes_file_genex_build}) + + set_target_properties(${target} PROPERTIES + INTERFACE_QT_MODULE_HAS_META_TYPES YES + # The property name is a bit misleading, it's not wrapped in a genex. + INTERFACE_QT_META_TYPES_BUILD_FILE "${metatypes_file_path}" + INTERFACE_QT_META_TYPES_FILE_NAME "${metatypes_file_name}" + ) +endfunction() + +# Same as above, but with $<INSTALL_INTERFACE>. +function(_qt_internal_assign_install_metatypes_files_and_properties target) + get_target_property(existing_meta_types_file ${target} INTERFACE_QT_META_TYPES_INSTALL_FILE) + if (existing_meta_types_file) + return() + endif() + + set(args_option + ) + set(args_single + INSTALL_DIR + ) + set(args_multi + ) + + cmake_parse_arguments(arg + "${args_option}" + "${args_single}" + "${args_multi}" ${ARGN}) + + + get_target_property(metatypes_file_name "${target}" INTERFACE_QT_META_TYPES_FILE_NAME) + + if(NOT metatypes_file_name) + message(FATAL_ERROR "INTERFACE_QT_META_TYPES_FILE_NAME of target ${target} is empty") + endif() + + if(NOT arg_INSTALL_DIR) + message(FATAL_ERROR "INSTALL_DIR must be specified") + endif() + + # Set up consumption of files via INTERFACE_SOURCES. + set(consumes_metatypes "$<BOOL:$<TARGET_PROPERTY:QT_CONSUMES_METATYPES>>") + + set(install_dir "${arg_INSTALL_DIR}") + + set(metatypes_file_install_path "${install_dir}/${metatypes_file_name}") + set(metatypes_file_install_path_genex "$<INSTALL_PREFIX>/${metatypes_file_install_path}") + set(metatypes_file_genex_install + "$<INSTALL_INTERFACE:$<${consumes_metatypes}:${metatypes_file_install_path_genex}>>" + ) + target_sources(${target} INTERFACE ${metatypes_file_genex_install}) + + set_target_properties(${target} PROPERTIES + INTERFACE_QT_META_TYPES_INSTALL_FILE "${metatypes_file_install_path}" + ) +endfunction() + + function(qt6_extract_metatypes target) get_target_property(existing_meta_types_file ${target} INTERFACE_QT_META_TYPES_BUILD_FILE) @@ -949,6 +1272,9 @@ function(qt6_extract_metatypes target) 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) @@ -964,13 +1290,13 @@ function(qt6_extract_metatypes target) set(cmake_autogen_cache_file "${target_binary_dir}/CMakeFiles/${target}_autogen.dir/ParseCache.txt") set(multi_config_args - --cmake-autogen-include-dir-path "${target_binary_dir}/${target}_autogen/include" + --cmake-autogen-include-dir-path "${target_autogen_build_dir}/include" ) else() set(cmake_autogen_cache_file "${target_binary_dir}/CMakeFiles/${target}_autogen.dir/ParseCache_$<CONFIG>.txt") set(multi_config_args - --cmake-autogen-include-dir-path "${target_binary_dir}/${target}_autogen/include_$<CONFIG>" + --cmake-autogen-include-dir-path "${target_autogen_build_dir}/include_$<CONFIG>" "--cmake-multi-config") endif() @@ -979,8 +1305,15 @@ function(qt6_extract_metatypes target) 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) + if(CMAKE_GENERATOR STREQUAL "Ninja" OR + CMAKE_GENERATOR STREQUAL "Ninja Multi-Config" OR + (CMAKE_GENERATOR MATCHES "Makefiles" AND + CMAKE_VERSION VERSION_GREATER_EQUAL "3.28")) + if(DEFINED QT_USE_CMAKE_DEPFILES) + set(use_dep_files ${QT_USE_CMAKE_DEPFILES}) + else() + set(use_dep_files TRUE) + endif() endif() endif() @@ -1031,9 +1364,18 @@ function(qt6_extract_metatypes target) add_dependencies(${target}_automoc_json_extraction ${target}_autogen) _qt_internal_assign_to_internal_targets_folder(${target}_automoc_json_extraction) else() - set(cmake_autogen_timestamp_file - "${target_binary_dir}/${target}_autogen/timestamp" - ) + set(timestamp_file "${target_autogen_build_dir}/timestamp") + set(timestamp_file_with_config "${timestamp_file}_$<CONFIG>") + if (is_multi_config AND CMAKE_VERSION VERSION_GREATER_EQUAL "3.29" + AND NOT QT_INTERNAL_USE_OLD_AUTOGEN_GRAPH_MULTI_CONFIG_METATYPES) + string(JOIN "" timestamp_genex + "$<IF:$<BOOL:$<TARGET_PROPERTY:${target}," + "AUTOGEN_BETTER_GRAPH_MULTI_CONFIG>>," + "${timestamp_file_with_config},${timestamp_file}>") + set(cmake_autogen_timestamp_file "${timestamp_genex}") + else() + set(cmake_autogen_timestamp_file ${timestamp_file}) + endif() add_custom_command(OUTPUT ${type_list_file} DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::cmake_automoc_parser @@ -1101,6 +1443,7 @@ function(qt6_extract_metatypes target) 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 @@ -1113,6 +1456,17 @@ function(qt6_extract_metatypes target) VERBATIM ) + if(CMAKE_GENERATOR MATCHES " 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") @@ -1132,52 +1486,42 @@ function(qt6_extract_metatypes target) 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 "$<BOOL:$<TARGET_PROPERTY:QT_CONSUMES_METATYPES>>") - set(metatypes_file_genex_build - "$<BUILD_INTERFACE:$<${consumes_metatypes}:${metatypes_file}>>" - ) - 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() + # Propagate the build time metatypes file. + _qt_internal_assign_build_metatypes_files_and_properties( + "${target}" + METATYPES_FILE_NAME "${metatypes_file_name}" + METATYPES_FILE_PATH "${metatypes_file}" + ) - # 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() + if(arg___QT_INTERNAL_INSTALL) + set(internal_install_option "INTERNAL_INSTALL") else() - set(install_dir "${arg___QT_INTERNAL_INSTALL_DIR}") + set(internal_install_option "") endif() + # TODO: Clean up Qt-specific installation not to happen in the public api. + # Check whether the metatype files should be installed. + _qt_internal_should_install_metatypes("${target}" + ${internal_install_option} + OUT_VAR should_install + ) + if(should_install) - set(metatypes_file_install_path "${install_dir}/${metatypes_file_name}") - set(metatypes_file_install_path_genex "$<INSTALL_PREFIX>/${metatypes_file_install_path}") - set(metatypes_file_genex_install - "$<INSTALL_INTERFACE:$<${consumes_metatypes}:${metatypes_file_install_path_genex}>>" + _qt_internal_get_metatypes_install_dir( + "${arg___QT_INTERNAL_INSTALL_DIR}" + "${INSTALL_ARCHDATADIR}" + install_dir + ) + + # Propagate the install time metatypes file. + _qt_internal_assign_install_metatypes_files_and_properties( + "${target}" + INSTALL_DIR "${install_dir}" ) - target_sources(${target} INTERFACE ${metatypes_file_genex_install}) install(FILES "${metatypes_file}" DESTINATION "${install_dir}") endif() endfunction() @@ -1669,11 +2013,29 @@ function(__qt_propagate_generated_resource target resource_name generated_source math(EXPR resource_count "${resource_count} + 1") set_target_properties(${target} PROPERTIES _qt_generated_resource_target_count ${resource_count}) + __qt_internal_generate_init_resource_source_file( + resource_init_file ${target} ${resource_name}) + set(resource_target "${target}_resources_${resource_count}") - add_library("${resource_target}" OBJECT "${generated_source_code}") + add_library("${resource_target}" OBJECT "${resource_init_file}") + set_target_properties(${resource_target} PROPERTIES + AUTOMOC FALSE + AUTOUIC FALSE + AUTORCC FALSE + ) + # Needed so that qtsymbolmacros.h and its dependent headers are already created / syncqt'ed. + if(TARGET Core_sync_headers) + set(headers_available_target "Core_sync_headers") + else() + set(headers_available_target "${QT_CMAKE_EXPORT_NAMESPACE}::Core") + endif() + add_dependencies(${resource_target} ${headers_available_target}) target_compile_definitions("${resource_target}" PRIVATE "$<TARGET_PROPERTY:${QT_CMAKE_EXPORT_NAMESPACE}::Core,INTERFACE_COMPILE_DEFINITIONS>" ) + target_include_directories("${resource_target}" PRIVATE + "$<TARGET_PROPERTY:${QT_CMAKE_EXPORT_NAMESPACE}::Core,INTERFACE_INCLUDE_DIRECTORIES>" + ) _qt_internal_set_up_static_runtime_library("${resource_target}") # Special handling is required for the Core library resources. The linking of the Core @@ -1692,7 +2054,7 @@ function(__qt_propagate_generated_resource target resource_name generated_source # .rcc/qrc_qprintdialog.cpp file(RELATIVE_PATH generated_cpp_file_relative_path "${CMAKE_CURRENT_BINARY_DIR}" - "${generated_source_code}") + "${resource_init_file}") set_property(TARGET ${resource_target} APPEND PROPERTY _qt_resource_generated_cpp_relative_path "${generated_cpp_file_relative_path}") @@ -1706,8 +2068,39 @@ function(__qt_propagate_generated_resource target resource_name generated_source set(${output_generated_target} "${resource_target}" PARENT_SCOPE) else() set(${output_generated_target} "" PARENT_SCOPE) - target_sources(${target} PRIVATE ${generated_source_code}) endif() + + target_sources(${target} PRIVATE ${generated_source_code}) +endfunction() + +function(__qt_internal_sanitize_resource_name out_var name) + # The sanitized output should match RCCResourceLibrary::writeInitializer()'s + # isAsciiLetterOrNumber-based substituion. + # MAKE_C_IDENTIFIER matches that, it replaces non-alphanumeric chars with underscores. + string(MAKE_C_IDENTIFIER "${name}" sanitized_resource_name) + set(${out_var} "${sanitized_resource_name}" PARENT_SCOPE) +endfunction() + +function(__qt_internal_generate_init_resource_source_file out_var target resource_name) + set(template_file "${__qt_core_macros_module_base_dir}/Qt6CoreResourceInit.in.cpp") + + # Gets replaced in the template + __qt_internal_sanitize_resource_name(RESOURCE_NAME "${resource_name}") + set(resource_init_path "${CMAKE_CURRENT_BINARY_DIR}/.qt/rcc/qrc_${resource_name}_init.cpp") + + configure_file("${template_file}" "${resource_init_path}" @ONLY) + + set(scope_args "") + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18") + set(scope_args TARGET_DIRECTORY ${target}) + endif() + set_source_files_properties(${resource_init_path} ${scope_args} PROPERTIES + SKIP_AUTOGEN TRUE + SKIP_UNITY_BUILD_INCLUSION TRUE + SKIP_PRECOMPILE_HEADERS TRUE + ) + + set(${out_var} "${resource_init_path}" PARENT_SCOPE) endfunction() # Make file visible in IDEs. @@ -1887,8 +2280,9 @@ function(_qt_internal_process_resource target resourceName) endif() return() endif() - set(generatedResourceFile "${CMAKE_CURRENT_BINARY_DIR}/.rcc/${resourceName}.qrc") + set(generatedResourceFile "${CMAKE_CURRENT_BINARY_DIR}/.qt/rcc/${resourceName}.qrc") _qt_internal_expose_source_file_to_ide(${target} ${generatedResourceFile}) + set_source_files_properties(${generatedResourceFile} PROPERTIES GENERATED TRUE) # Generate .qrc file: @@ -1983,9 +2377,9 @@ function(_qt_internal_process_resource target resourceName) endif() endif() elseif(rcc_BIG_RESOURCES) - set(generatedOutfile "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qrc_${resourceName}_tmp.cpp") + set(generatedOutfile "${CMAKE_CURRENT_BINARY_DIR}/.qt/rcc/qrc_${resourceName}_tmp.cpp") else() - set(generatedOutfile "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qrc_${resourceName}.cpp") + set(generatedOutfile "${CMAKE_CURRENT_BINARY_DIR}/.qt/rcc/qrc_${resourceName}.cpp") endif() set(pass_msg) @@ -2024,6 +2418,9 @@ function(_qt_internal_process_resource target resourceName) 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) @@ -2037,7 +2434,7 @@ function(_qt_internal_process_resource target resourceName) if(rcc_BIG_RESOURCES) set(pass1OutputFile ${generatedOutfile}) set(generatedOutfile - "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qrc_${resourceName}${CMAKE_CXX_OUTPUT_EXTENSION}") + "${CMAKE_CURRENT_BINARY_DIR}/.qt/rcc/qrc_${resourceName}${CMAKE_CXX_OUTPUT_EXTENSION}") _qt_internal_add_rcc_pass2( RESOURCE_NAME ${resourceName} RCC_OPTIONS ${rccArgsAllPasses} @@ -2300,14 +2697,36 @@ function(_qt_internal_add_library target) # 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) + if(DEFINED BUILD_SHARED_LIBS AND NOT QT_BUILDING_QT AND NOT QT_BUILD_STANDALONE_TESTS) + __qt_internal_setup_policy(QTP0003 "6.7.0" + "BUILD_SHARED_LIBS is set to ${BUILD_SHARED_LIBS} but it has no effect on\ + default library type created by Qt CMake API commands. The default library type\ + is set to the Qt build type.\ + This behavior can be changed by setting QTP0003 to NEW.\ + Check https://doc.qt.io/qt-6/qt-cmake-policy-qtp0003.html for policy details." + ) + qt6_policy(GET QTP0003 build_shared_libs_policy) + else() + set(build_shared_libs_policy "") + endif() + + if(build_shared_libs_policy STREQUAL "NEW" OR QT_BUILDING_QT OR QT_BUILD_STANDALONE_TESTS) + if(BUILD_SHARED_LIBS OR (NOT DEFINED BUILD_SHARED_LIBS AND QT6_IS_SHARED_LIBS_BUILD)) + set(type_to_create SHARED) + else() + set(type_to_create STATIC) + endif() else() - set(type_to_create STATIC) + if(QT6_IS_SHARED_LIBS_BUILD) + set(type_to_create SHARED) + else() + set(type_to_create STATIC) + endif() endif() endif() add_library(${target} ${type_to_create} ${arg_UNPARSED_ARGUMENTS}) + _qt_internal_disable_autorcc_zstd_when_not_supported("${target}") _qt_internal_set_up_static_runtime_library(${target}) if(NOT type_to_create STREQUAL "INTERFACE" AND NOT type_to_create STREQUAL "OBJECT") @@ -2508,6 +2927,22 @@ function(_qt_internal_setup_deploy_support) set(target ${aliased_target}) endif() + # Generate deployment information for each target if the CMake version is recent enough. + # The call is deferred to have all targets of the projects available. + if(CMAKE_VERSION GREATER_EQUAL "3.19.0") + if(is_multi_config) + set(targets_file "${deploy_impl_dir}/QtDeployTargets-$<CONFIG>.cmake") + else() + set(targets_file "${deploy_impl_dir}/QtDeployTargets.cmake") + endif() + cmake_language(EVAL CODE + "cmake_language(DEFER + DIRECTORY [[${CMAKE_SOURCE_DIR}]] + CALL _qt_internal_write_target_deploy_info [[${targets_file}]])" + ) + _qt_internal_add_deploy_support("${targets_file}") + 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. @@ -2546,6 +2981,18 @@ function(_qt_internal_setup_deploy_support) set(__QT_DEPLOY_TOOL "") endif() + # Determine whether this is a multi-config build with a Debug configuration. + set(is_multi_config_build_with_debug_config FALSE) + get_target_property(target_is_imported ${target} IMPORTED) + if(target_is_imported) + get_target_property(target_imported_configs ${target} IMPORTED_CONFIGURATIONS) + list(LENGTH target_imported_configs target_imported_configs_length) + if(target_imported_configs_length GREATER "1" + AND "DEBUG" IN_LIST target_imported_configs) + set(is_multi_config_build_with_debug_config TRUE) + endif() + endif() + _qt_internal_add_deploy_support("${CMAKE_CURRENT_LIST_DIR}/Qt6CoreDeploySupport.cmake") set(deploy_ignored_lib_dirs "") @@ -2593,6 +3040,43 @@ function(_qt_internal_setup_deploy_support) endif() endif() + # Generate path to the target (not host) qtpaths file. Needed for windeployqt when + # cross-compiling from an x86_64 host to an arm64 target, so it knows which architecture + # libraries should be deployed. + if(CMAKE_HOST_WIN32) + if(CMAKE_CROSSCOMPILING) + set(qt_paths_ext ".bat") + else() + set(qt_paths_ext ".exe") + endif() + else() + set(qt_paths_ext "") + endif() + + + + set(target_qtpaths_path "") + set(qtpaths_prefix "${QT6_INSTALL_PREFIX}/${QT6_INSTALL_BINS}") + get_property(qt_major_version TARGET "${target}" PROPERTY INTERFACE_QT_MAJOR_VERSION) + if(qt_major_version) + set(target_qtpaths_with_major_version_path + "${qtpaths_prefix}/qtpaths${qt_major_version}${qt_paths_ext}") + if(EXISTS "${target_qtpaths_with_major_version_path}") + set(target_qtpaths_path "${target_qtpaths_with_major_version_path}") + endif() + endif() + + if(NOT target_qtpaths_path) + set(target_qtpaths_path_without_version "${qtpaths_prefix}/qtpaths${qt_paths_ext}") + if(EXISTS "${target_qtpaths_path_without_version}") + set(target_qtpaths_path "${target_qtpaths_path_without_version}") + endif() + endif() + + if(NOT target_qtpaths_path) + message(DEBUG "No qtpaths executable found for deployment purposes.") + endif() + file(GENERATE OUTPUT "${QT_DEPLOY_SUPPORT}" CONTENT "cmake_minimum_required(VERSION 3.16...3.21) @@ -2601,6 +3085,9 @@ function(_qt_internal_setup_deploy_support) if(NOT QT_DEPLOY_BIN_DIR) set(QT_DEPLOY_BIN_DIR \"${CMAKE_INSTALL_BINDIR}\") endif() +if(NOT QT_DEPLOY_LIBEXEC_DIR) + set(QT_DEPLOY_LIBEXEC_DIR \"${CMAKE_INSTALL_LIBEXECDIR}\") +endif() if(NOT QT_DEPLOY_LIB_DIR) set(QT_DEPLOY_LIB_DIR \"${CMAKE_INSTALL_LIBDIR}\") endif() @@ -2637,16 +3124,21 @@ 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_DATA \"${QT6_INSTALL_DATA}\") +set(__QT_DEPLOY_QT_INSTALL_LIBEXECS \"${QT6_INSTALL_LIBEXECS}\") set(__QT_DEPLOY_QT_INSTALL_PLUGINS \"${QT6_INSTALL_PLUGINS}\") set(__QT_DEPLOY_QT_INSTALL_TRANSLATIONS \"${QT6_INSTALL_TRANSLATIONS}\") +set(__QT_DEPLOY_TARGET_QT_PATHS_PATH \"${target_qtpaths_path}\") 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}\") +set(__QT_DEPLOY_QT_IS_MULTI_CONFIG_BUILD_WITH_DEBUG \"${is_multi_config_build_with_debug_config}\") +set(__QT_DEPLOY_QT_DEBUG_POSTFIX \"${QT6_DEBUG_POSTFIX}\") # Define the CMake commands to be made available during deployment. set(__qt_deploy_support_files - \"$<JOIN:$<TARGET_PROPERTY:${target},_qt_deploy_support_files>,\" + \"$<JOIN:$<TARGET_GENEX_EVAL:${target},$<TARGET_PROPERTY:${target},_qt_deploy_support_files>>,\" \">\" ) foreach(__qt_deploy_support_file IN LISTS __qt_deploy_support_files) @@ -2658,6 +3150,142 @@ unset(__qt_deploy_support_files) ") endfunction() +# Write deployment information for the targets of the project. +function(_qt_internal_write_target_deploy_info out_file) + set(targets "") + set(dynamic_target_types EXECUTABLE SHARED_LIBRARY MODULE_LIBRARY) + _qt_internal_collect_buildsystem_targets(targets + "${CMAKE_SOURCE_DIR}" INCLUDE ${dynamic_target_types} STATIC_LIBRARY) + set(content "") + foreach(target IN LISTS targets) + set(var_prefix "__QT_DEPLOY_TARGET_${target}") + string(APPEND content "set(${var_prefix}_FILE $<TARGET_FILE:${target}>)\n") + get_target_property(target_type ${target} TYPE) + string(APPEND content "set(${var_prefix}_TYPE ${target_type})\n") + if(WIN32 AND CMAKE_VERSION GREATER_EQUAL "3.21" + AND target_type IN_LIST dynamic_target_types) + string(APPEND content + "set(${var_prefix}_RUNTIME_DLLS $<TARGET_RUNTIME_DLLS:${target}>)\n") + endif() + endforeach() + file(GENERATE OUTPUT "${out_file}" CONTENT "${content}") +endfunction() + +function(_qt_internal_is_examples_deployment_supported_in_current_config out_var out_var_reason) + # Deployment API doesn't work when examples / tests are built in-tree of a prefix qt build. + if(QT_BUILDING_QT AND QT_WILL_INSTALL AND NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + set(deployment_supported FALSE) + set(not_supported_reason "PREFIX_BUILD") + else() + set(deployment_supported TRUE) + set(not_supported_reason "") + endif() + + set(${out_var} "${deployment_supported}" PARENT_SCOPE) + set(${out_var_reason} "${not_supported_reason}" PARENT_SCOPE) +endfunction() + +function(_qt_internal_should_skip_deployment_api out_var out_var_reason) + set(skip_deployment FALSE) + _qt_internal_is_examples_deployment_supported_in_current_config( + deployment_supported + not_supported_reason + ) + + # Allow opting out of deployment, so that we can add deployment api to all our examples, + # but only run it in the CI for a select few, to avoid the overhead of deploying all examples. + if(QT_INTERNAL_SKIP_DEPLOYMENT OR (NOT deployment_supported)) + set(skip_deployment TRUE) + endif() + + set(reason "") + if(NOT deployment_supported) + set(reason "${not_supported_reason}") + elseif(QT_INTERNAL_SKIP_DEPLOYMENT) + set(reason "SKIP_REQUESTED") + endif() + + set(${out_var} "${skip_deployment}" PARENT_SCOPE) + set(${out_var_reason} "${reason}" PARENT_SCOPE) +endfunction() + +function(_qt_internal_should_skip_post_build_deployment_api out_var out_var_reason) + set(skip_deployment FALSE) + set(deployment_supported TRUE) + + # Allow opting out of deployment, so that we can add deployment api to all our examples, + # but only run it in the CI for a select few, to avoid the overhead of deploying all examples. + if(QT_INTERNAL_SKIP_DEPLOYMENT OR (NOT deployment_supported)) + set(skip_deployment TRUE) + endif() + + set(reason "") + if(NOT deployment_supported) + set(reason "REASON_UNSPECIFIED") + elseif(QT_INTERNAL_SKIP_DEPLOYMENT) + set(reason "SKIP_REQUESTED") + endif() + + set(${out_var} "${skip_deployment}" PARENT_SCOPE) + set(${out_var_reason} "${reason}" PARENT_SCOPE) +endfunction() + +# Generate a deploy script that does nothing aside from showing a warning message. +# The warning can be hidden by setting the QT_INTERNAL_HIDE_NO_OP_DEPLOYMENT_WARNING variable. +function(_qt_internal_generate_no_op_deploy_script) + set(no_value_options + ) + set(single_value_options + FUNCTION_NAME + NAME + OUTPUT_SCRIPT + SKIP_REASON + TARGET + ) + set(multi_value_options + ) + + cmake_parse_arguments(PARSE_ARGV 0 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + + if(NOT arg_OUTPUT_SCRIPT) + message(FATAL_ERROR "No OUTPUT_SCRIPT option specified") + endif() + if(NOT arg_FUNCTION_NAME) + message(FATAL_ERROR "No FUNCTION_NAME option specified") + endif() + + set(generate_args "") + if(arg_NAME) + list(APPEND generate_args NAME "${arg_NAME}") + endif() + if(arg_TARGET) + list(APPEND generate_args TARGET "${arg_TARGET}") + endif() + + set(function_name "${arg_FUNCTION_NAME}") + + # The empty space is required, otherwise + set(content " +message(DEBUG \"Running no-op deployment script because QT_INTERNAL_SKIP_DEPLOYMENT was ON.\") +") + if(NOT QT_INTERNAL_HIDE_NO_OP_DEPLOYMENT_WARNING AND arg_SKIP_REASON STREQUAL "PREFIX_BUILD") + set(content " +message(STATUS \"${function_name}(TARGET ${arg_TARGET}) is a no-op for prefix \" +\"non-standalone builds due to various issues. Consider using a -no-prefix build \" +\"or qt-internal-configure-tests or qt-internal-configure-examples if you want deployment to run.\") +") + endif() + + qt6_generate_deploy_script( + ${generate_args} + OUTPUT_SCRIPT deploy_script + CONTENT "${content}") + + set("${arg_OUTPUT_SCRIPT}" "${deploy_script}" PARENT_SCOPE) +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 @@ -2724,10 +3352,13 @@ macro(qt6_standard_project_setup) set(__qt_sps_args_option) set(__qt_sps_args_single - MIN_VERSION - MAX_VERSION + REQUIRES + SUPPORTS_UP_TO + I18N_SOURCE_LANGUAGE + ) + set(__qt_sps_args_multi + I18N_TRANSLATED_LANGUAGES ) - set(__qt_sps_args_multi) cmake_parse_arguments(__qt_sps_arg "${__qt_sps_args_option}" "${__qt_sps_args_single}" @@ -2750,22 +3381,22 @@ macro(qt6_standard_project_setup) else() message(FATAL_ERROR "Can not determine Qt version.") endif() - if(__qt_sps_arg_MIN_VERSION) - if("${__qt_current_version}" VERSION_LESS "${__qt_sps_arg_MIN_VERSION}") + 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_MIN_VERSION}, " + "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_MIN_VERSION}") + set(__qt_policy_check_version "${__qt_sps_arg_REQUIRES}") endif() - if(__qt_sps_arg_MAX_VERSION) - if(__qt_sps_arg_MIN_VERSION) - if(${__qt_sps_arg_MAX_VERSION} VERSION_LESS ${__qt_sps_arg_MIN_VERSION}) - message(FATAL_ERROR "MAX_VERSION must be larger than or equal to MIN_VERSION.") + 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_MAX_VERSION}") + set(__qt_policy_check_version "${__qt_sps_arg_SUPPORTS_UP_TO}") else() - message(FATAL_ERROR "Please specify the MIN_VERSION as well.") + message(FATAL_ERROR "Please specify the REQUIRES as well.") endif() endif() @@ -2811,8 +3442,8 @@ macro(qt6_standard_project_setup) endif() endforeach() - # Enable folder support for IDEs. A future CMake version might enable this by default. - # See CMake issue #21695. + # 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) @@ -2821,11 +3452,50 @@ macro(qt6_standard_project_setup) 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_FOLDERS) + 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() + + # Hide generated files in dedicated folder. Unfortunately we can't use a + # top level "Generated Files" folder for this, as CMake will then put the + # folder first in the list of folders, whereas we want to keep Sources and + # Headers front and center. See also _qt_internal_finalize_source_groups + set_property(GLOBAL PROPERTY AUTOGEN_SOURCE_GROUP "Source Files/Generated") + + # Treat metatypes JSON files as generated. We propagate these INTERFACE_SOURCES, + # due to CMake's lack of a generic mechanism for property inheritance (see + # https://gitlab.kitware.com/cmake/cmake/-/issues/20416), but we don't want + # them to clutter up the user's project. + source_group("Source Files/Generated" REGULAR_EXPRESSION "(_metatypes\\.json)$") + + # I18N support. + if(DEFINED __qt_sps_arg_I18N_TRANSLATED_LANGUAGES + AND NOT DEFINED QT_I18N_TRANSLATED_LANGUAGES) + set(QT_I18N_TRANSLATED_LANGUAGES ${__qt_sps_arg_I18N_TRANSLATED_LANGUAGES}) + endif() + if(NOT DEFINED __qt_sps_arg_I18N_SOURCE_LANGUAGE) + set(__qt_sps_arg_I18N_SOURCE_LANGUAGE en) + endif() + if(NOT DEFINED QT_I18N_SOURCE_LANGUAGE) + set(QT_I18N_SOURCE_LANGUAGE ${__qt_sps_arg_I18N_SOURCE_LANGUAGE}) + endif() + + if(CMAKE_GENERATOR STREQUAL "Xcode") + # Ensure we always use device SDK for Xcode for single-arch Qt builds + set(qt_osx_arch_count 0) + if(QT_OSX_ARCHITECTURES) + list(LENGTH QT_OSX_ARCHITECTURES qt_osx_arch_count) + endif() + if(NOT qt_osx_arch_count GREATER 1 AND ${CMAKE_OSX_SYSROOT} MATCHES "^[a-z]+simulator$") + # Xcode expects the base SDK to be the device SDK + set(simulator_sysroot "${CMAKE_OSX_SYSROOT}") + string(REGEX REPLACE "simulator" "os" CMAKE_OSX_SYSROOT "${CMAKE_OSX_SYSROOT}") + set(CMAKE_OSX_SYSROOT "${CMAKE_OSX_SYSROOT}" CACHE STRING "" FORCE) + set(CMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS "${simulator_sysroot}") + endif() + endif() endif() endmacro() @@ -2838,6 +3508,40 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) endmacro() endif() +# Store in ${out_var} the i18n catalogs that belong to the passed Qt modules. +# The catalog "qtbase" is always added to the result. +# +# Example: +# _qt_internal_get_i18n_catalogs_for_modules(catalogs Quick Help) +# catalogs -> qtbase;qtdeclarative;qt_help +function(_qt_internal_get_i18n_catalogs_for_modules out_var) + set(result "qtbase") + set(modules "${ARGN}") + set(module_catalog_mapping + "Bluetooth|Nfc" qtconnectivity + "Help" qt_help + "Multimedia(Widgets|QuickPrivate)?" qtmultimedia + "Qml|Quick" qtdeclarative + "SerialPort" qtserialport + "WebEngine" qtwebengine + "WebSockets" qtwebsockets + ) + list(LENGTH module_catalog_mapping max_i) + math(EXPR max_i "${max_i} - 1") + foreach(module IN LISTS modules) + foreach(i RANGE 0 ${max_i} 2) + list(GET module_catalog_mapping ${i} module_rex) + if(NOT module MATCHES "^(${module_rex})") + continue() + endif() + math(EXPR k "${i} + 1") + list(GET module_catalog_mapping ${k} catalog) + list(APPEND result ${catalog}) + endforeach() + endforeach() + set("${out_var}" "${result}" PARENT_SCOPE) +endfunction() + function(qt6_generate_deploy_script) set(no_value_options "") set(single_value_options @@ -2896,6 +3600,13 @@ function(qt6_generate_deploy_script) # 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 @@ -2920,9 +3631,10 @@ function(qt6_generate_deploy_script) string(APPEND deploy_script "${config_infix}.cmake") set(${arg_OUTPUT_SCRIPT} "${deploy_script}" PARENT_SCOPE) + _qt_internal_get_i18n_catalogs_for_modules(catalogs ${QT_ALL_MODULES_FOUND_VIA_FIND_PACKAGE}) 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}\") +set(__QT_DEPLOY_I18N_CATALOGS \"${catalogs}\") ") list(TRANSFORM arg_CONTENT REPLACE "\\$" "\$") file(GENERATE OUTPUT ${deploy_script} CONTENT "${boiler_plate}${arg_CONTENT}") @@ -2947,6 +3659,7 @@ function(qt6_generate_deploy_app_script) # mutually exclusive with the TARGET keyword. set(no_value_options NO_TRANSLATIONS + NO_COMPILER_RUNTIME NO_UNSUPPORTED_PLATFORM_ERROR ) set(single_value_options @@ -2959,6 +3672,7 @@ function(qt6_generate_deploy_app_script) ) set(qt_deploy_runtime_dependencies_options # These options are forwarded as is to qt_deploy_runtime_dependencies. + DEPLOY_TOOL_OPTIONS PRE_INCLUDE_REGEXES PRE_EXCLUDE_REGEXES POST_INCLUDE_REGEXES @@ -2998,12 +3712,25 @@ function(qt6_generate_deploy_app_script) message(FATAL_ERROR "OUTPUT_SCRIPT must be specified") endif() + get_target_property(is_bundle ${arg_TARGET} MACOSX_BUNDLE) + + set(unsupported_platform_extra_message "") 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() + + if(NOT is_bundle) + string(APPEND qt_build_type_string ", non-bundle app") + set(unsupported_platform_extra_message + "Executable targets have to be app bundles to use this command on Apple platforms.") + endif() + set(generate_args TARGET ${arg_TARGET} OUTPUT_SCRIPT deploy_script @@ -3013,6 +3740,9 @@ function(qt6_generate_deploy_app_script) 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) @@ -3021,15 +3751,16 @@ function(qt6_generate_deploy_app_script) 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() + _qt_internal_should_skip_deployment_api(skip_deployment skip_reason) + if(skip_deployment) + _qt_internal_generate_no_op_deploy_script( + FUNCTION_NAME "qt6_generate_deploy_app_script" + SKIP_REASON "${skip_reason}" + ${generate_args} + ) + elseif(APPLE AND NOT IOS AND QT6_IS_SHARED_LIBS_BUILD AND is_bundle) + # TODO: Consider handling non-bundle applications in the future using the generic cmake + # runtime dependency feature. qt6_generate_deploy_script(${generate_args} CONTENT " qt6_deploy_runtime_dependencies( @@ -3046,7 +3777,8 @@ qt6_deploy_runtime_dependencies( ${common_deploy_args}) ") - elseif(UNIX AND NOT APPLE AND NOT ANDROID AND QT6_IS_SHARED_LIBS_BUILD) + 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( @@ -3057,19 +3789,22 @@ ${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})." + "this target platform (${CMAKE_SYSTEM_NAME}, ${qt_build_type_string}). " + ${unsupported_platform_extra_message} ) else() - qt6_generate_deploy_script(${generate_args} - CONTENT " -_qt_internal_show_skip_runtime_deploy_message(\"${qt_build_type_string}\") -") + set(skip_message + "_qt_internal_show_skip_runtime_deploy_message(\"${qt_build_type_string}\"") + if(unsupported_platform_extra_message) + string(APPEND skip_message + "\n EXTRA_MESSAGE \"${unsupported_platform_extra_message}\"") + endif() + string(APPEND skip_message "\n)") + qt6_generate_deploy_script(${generate_args} CONTENT "${skip_message}") endif() set(${arg_OUTPUT_SCRIPT} "${deploy_script}" PARENT_SCOPE) |