summaryrefslogtreecommitdiffstats
path: root/cmake
diff options
context:
space:
mode:
authorAlexey Edelev <alexey.edelev@qt.io>2022-08-15 18:29:41 +0200
committerAlexey Edelev <alexey.edelev@qt.io>2022-09-27 13:12:11 +0200
commitb89d63515bb352cecfd87e709320a2db5b6a1906 (patch)
tree4f6e40d6e14991af1c9082ab7508a49a6360f25a /cmake
parent458ec4cb5442315c2c923ba78faf45fdd729a109 (diff)
Replace the syncqt.pl script with syncqt tool
syncqt.pl adds an extra dependency on perl when building Qt. Modern C++ provides the convenient cross-platform way to access a filesystem and to use regular expressions, so we may replace the perl script with C++ application. The syncqt executable is built at configure time and installed as QtCore tool. It's running at configure time to deliver the required header files for IDE to build a consistent code model and at the build time to keep tracking changes in header files and generate the missing aliases without reconfiguring. 'syncqt' only parses header files from a CMake build tree, so the resulting Qt installation only contains interfacing headers that belong to the platform that Qt is built for. 'sync.profile' files are not used as the 'source of truth' for sync qt procedure anymore, all the necessary information is taken from either CMake files at configure time or from the module header files while parsing them. syncqt.pl is still in place since it's required as fallback solution for a smooth transition to the new syncqt implementation for all qt repositories. This patchset only enables the C++ based syncqt for 'qtbase' repository. From the performance perspective C++ version works faster then perl script, also the configure time is reduced significally on subsequent reconfigurations - up x2 times faster when re-configuring repository, but it also takes time to compile the tool itself the first time. Numbers for qtbase: syncqt.pl syncqt.cpp initial: 0m16,035s 0m20,413s reconfig: 0m6,819s 0m3,725s The syncing procedure can be run separately for each module using <ModuleName>_sync_headers targets. The 'sync_headers' target can be used to sync all the modules at once. Task-number: QTBUG-87480 Task-number: QTBUG-103196 Change-Id: I8c938bcaf88a8713b39bbfd66d9e7ef12b2c3523 Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'cmake')
-rw-r--r--cmake/QtBaseGlobalTargets.cmake1
-rw-r--r--cmake/QtBuild.cmake9
-rw-r--r--cmake/QtBuildInternals/QtBuildInternalsConfig.cmake14
-rw-r--r--cmake/QtDocsHelpers.cmake6
-rw-r--r--cmake/QtExecutableHelpers.cmake2
-rw-r--r--cmake/QtFrameworkHelpers.cmake25
-rw-r--r--cmake/QtHeadersClean.cmake180
-rw-r--r--cmake/QtModuleHeadersCheck.cmake33
-rw-r--r--cmake/QtModuleHelpers.cmake156
-rw-r--r--cmake/QtPluginHelpers.cmake2
-rw-r--r--cmake/QtPostProcessHelpers.cmake8
-rw-r--r--cmake/QtSyncQtHelpers.cmake266
-rw-r--r--cmake/QtToolHelpers.cmake3
13 files changed, 548 insertions, 157 deletions
diff --git a/cmake/QtBaseGlobalTargets.cmake b/cmake/QtBaseGlobalTargets.cmake
index ba56ea1704..ac3b63a874 100644
--- a/cmake/QtBaseGlobalTargets.cmake
+++ b/cmake/QtBaseGlobalTargets.cmake
@@ -247,6 +247,7 @@ qt_copy_or_install(FILES
cmake/QtLalrHelpers.cmake
cmake/QtModuleConfig.cmake.in
cmake/QtModuleDependencies.cmake.in
+ cmake/QtModuleHeadersCheck.cmake
cmake/QtModuleHelpers.cmake
cmake/QtModuleToolsConfig.cmake.in
cmake/QtModuleToolsDependencies.cmake.in
diff --git a/cmake/QtBuild.cmake b/cmake/QtBuild.cmake
index a99bb20c4a..2db677bef8 100644
--- a/cmake/QtBuild.cmake
+++ b/cmake/QtBuild.cmake
@@ -228,6 +228,15 @@ if(NOT QT_MKSPECS_DIR)
set(QT_MKSPECS_DIR "${QT_MKSPECS_DIR}" CACHE INTERNAL "")
endif()
+# macOS versions 10.14 and less don't have the implementation of std::filesystem API.
+if(CMAKE_HOST_APPLE AND CMAKE_HOST_SYSTEM_VERSION VERSION_LESS "19.0.0")
+ message(FATAL_ERROR "macOS versions less than 10.15 are not supported for building Qt.")
+endif()
+
+if(NOT DEFINED QT_USE_SYNCQT_CPP)
+ set(QT_USE_SYNCQT_CPP FALSE)
+endif()
+
# the default RPATH to be used when installing, but only if it's not a system directory
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBDIR}" isSystemDir)
if("${isSystemDir}" STREQUAL "-1")
diff --git a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake
index 161c0bf5f1..fc978525a3 100644
--- a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake
+++ b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake
@@ -419,6 +419,10 @@ macro(qt_build_repo_begin)
add_dependencies(install_docs install_html_docs install_qch_docs)
endif()
+ if(NOT TARGET sync_headers)
+ add_custom_target(sync_headers)
+ endif()
+
# Add global qt_plugins, qpa_plugins and qpa_default_plugins convenience custom targets.
# Internal executables will add a dependency on the qpa_default_plugins target,
# so that building and running a test ensures it won't fail at runtime due to a missing qpa
@@ -474,6 +478,10 @@ macro(qt_build_repo_begin)
if(NOT TARGET benchmark)
add_custom_target(benchmark)
endif()
+
+ if(QT_INTERNAL_SYNCED_MODULES)
+ set_property(GLOBAL PROPERTY _qt_synced_modules ${QT_INTERNAL_SYNCED_MODULES})
+ endif()
endmacro()
macro(qt_build_repo_end)
@@ -510,6 +518,12 @@ macro(qt_build_repo_end)
if(NOT QT_SUPERBUILD)
qt_print_build_instructions()
endif()
+
+ get_property(synced_modules GLOBAL PROPERTY _qt_synced_modules)
+ if(synced_modules)
+ set(QT_INTERNAL_SYNCED_MODULES ${synced_modules} CACHE INTERNAL
+ "List of the synced modules. Prevents running syncqt.cpp after the first configuring.")
+ endif()
endmacro()
macro(qt_build_repo)
diff --git a/cmake/QtDocsHelpers.cmake b/cmake/QtDocsHelpers.cmake
index 53d1ab4be1..04114cb921 100644
--- a/cmake/QtDocsHelpers.cmake
+++ b/cmake/QtDocsHelpers.cmake
@@ -140,6 +140,12 @@ function(qt_internal_add_docs)
)
add_dependencies(prepare_docs_${target} qattributionsscanner_${target})
+ if(QT_USE_SYNCQT_CPP)
+ if(NOT TARGET sync_all_public_headers)
+ add_custom_target(sync_all_public_headers)
+ endif()
+ add_dependencies(prepare_docs_${target} sync_all_public_headers)
+ endif()
# generate docs target
set(generate_qdoc_args
diff --git a/cmake/QtExecutableHelpers.cmake b/cmake/QtExecutableHelpers.cmake
index b8bccdbb07..4bd03c8f8c 100644
--- a/cmake/QtExecutableHelpers.cmake
+++ b/cmake/QtExecutableHelpers.cmake
@@ -429,7 +429,7 @@ function(qt_internal_add_configure_time_executable target)
set(cmake_flags_arg)
if(arg_CMAKE_FLAGS)
- set(cmake_flags_arg CMAKE_FLAGS ${arg_CMAKE_FLAGS})
+ set(cmake_flags_arg CMAKE_FLAGS "${arg_CMAKE_FLAGS}")
endif()
configure_file("${template}" "${target_binary_dir}/CMakeLists.txt" @ONLY)
try_compile(result
diff --git a/cmake/QtFrameworkHelpers.cmake b/cmake/QtFrameworkHelpers.cmake
index 7effc579f6..3b4cb01223 100644
--- a/cmake/QtFrameworkHelpers.cmake
+++ b/cmake/QtFrameworkHelpers.cmake
@@ -97,6 +97,19 @@ function(qt_copy_framework_headers target)
QT_COPIED_FRAMEWORK_HEADERS "${out_files}")
endfunction()
+function(qt_internal_generate_fake_framework_header target)
+ # Hack to create the "Headers" symlink in the framework:
+ # Create a fake header file and copy it into the framework by marking it as PUBLIC_HEADER.
+ # CMake now takes care of creating the symlink.
+ set(fake_header "${CMAKE_CURRENT_BINARY_DIR}/${target}_fake_header.h")
+ qt_internal_get_main_cmake_configuration(main_config)
+ file(GENERATE OUTPUT "${fake_header}" CONTENT "// ignore this file\n"
+ CONDITION "$<CONFIG:${main_config}>")
+ target_sources(${target} PRIVATE "${fake_header}")
+ set_source_files_properties("${fake_header}" PROPERTIES GENERATED ON)
+ set_property(TARGET ${target} APPEND PROPERTY PUBLIC_HEADER "${fake_header}")
+endfunction()
+
function(qt_finalize_framework_headers_copy target)
get_target_property(target_type ${target} TYPE)
if(${target_type} STREQUAL "INTERFACE_LIBRARY")
@@ -108,17 +121,7 @@ function(qt_finalize_framework_headers_copy target)
endif()
get_target_property(headers ${target} QT_COPIED_FRAMEWORK_HEADERS)
if(headers)
- # Hack to create the "Headers" symlink in the framework:
- # Create a fake header file and copy it into the framework by marking it as PUBLIC_HEADER.
- # CMake now takes care of creating the symlink.
- set(fake_header ${target}_fake_header.h)
- qt_internal_get_main_cmake_configuration(main_config)
- file(GENERATE OUTPUT ${fake_header} CONTENT "// ignore this file\n"
- CONDITION "$<CONFIG:${main_config}>")
- string(PREPEND fake_header "${CMAKE_CURRENT_BINARY_DIR}/")
- target_sources(${target} PRIVATE ${fake_header})
- set_source_files_properties(${fake_header} PROPERTIES GENERATED ON)
- set_property(TARGET ${target} APPEND PROPERTY PUBLIC_HEADER ${fake_header})
+ qt_internal_generate_fake_framework_header(${target})
# Add a target, e.g. Core_framework_headers, that triggers the header copy.
add_custom_target(${target}_framework_headers DEPENDS ${headers})
diff --git a/cmake/QtHeadersClean.cmake b/cmake/QtHeadersClean.cmake
index 29385de5fd..6300a81832 100644
--- a/cmake/QtHeadersClean.cmake
+++ b/cmake/QtHeadersClean.cmake
@@ -4,26 +4,18 @@
# Add a custom ${module_target}_headersclean_check target that builds each header in
# ${module_headers} with a custom set of defines. This makes sure our public headers
# are self-contained, and also compile with more strict compiler options.
-function(qt_internal_add_headersclean_target
- module_target
- module_include_name
- module_headers)
- # module_headers is a list of strings of the form
- # <headerfile>[:feature]
+function(qt_internal_add_headersclean_target module_target module_headers)
+ get_target_property(has_headers ${module_target} _qt_module_has_headers)
+ if(NOT has_headers)
+ return()
+ endif()
+
set(hclean_headers "")
- foreach(entry ${module_headers})
- string(REPLACE ":" ";" entry_list ${entry})
- list(LENGTH entry_list entry_list_length)
- list(GET entry_list 0 entry_path)
-
- if (${entry_list_length} EQUAL 2)
- list(GET entry_list 1 entry_feature)
- if (NOT QT_FEATURE_${entry_feature})
- message(STATUS "headersclean: Ignoring header ${entry_path} because of missing feature ${entry_feature}")
- continue()
- endif()
+ foreach(header IN LISTS module_headers)
+ get_filename_component(header_name "${header}" NAME)
+ if(header_name MATCHES "^q[^_]+\\.h$" AND NOT header_name MATCHES ".*(global|exports)\\.h")
+ list(APPEND hclean_headers "${header}")
endif()
- list(APPEND hclean_headers ${entry_path})
endforeach()
# Make sure that the header compiles with our strict options
@@ -53,6 +45,12 @@ function(qt_internal_add_headersclean_target
set(target_includes_joined_genex
"$<${includes_exist_genex}:-I$<JOIN:${target_includes_genex},;-I>>")
+ get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
+ if(is_multi_config)
+ list(GET CMAKE_CONFIGURATION_TYPES 0 first_config_type)
+ set(config_suffix "$<$<NOT:$<CONFIG:${first_config_type}>>:-$<CONFIG>>")
+ endif()
+
# qmake doesn't seem to add the defines that are set by the header_only_module when checking the
# the cleanliness of the module's header files.
# This allows us to bypass an error with CMake 3.18 and lower when trying to evaluate
@@ -165,35 +163,23 @@ function(qt_internal_add_headersclean_target
endforeach()
endif()
- foreach(header ${hclean_headers})
- get_filename_component(input_path "${header}" ABSOLUTE)
- set(artifact_path "header_check/${header}.o")
- get_filename_component(artifact_directory "${artifact_path}" DIRECTORY)
- set(comment_header_path "${CMAKE_CURRENT_SOURCE_DIR}/${header}")
- file(RELATIVE_PATH comment_header_path "${PROJECT_SOURCE_DIR}" "${comment_header_path}")
-
- add_custom_command(
- OUTPUT "${artifact_path}"
- COMMENT "headersclean: Checking header ${comment_header_path}"
- COMMAND ${CMAKE_COMMAND} -E make_directory "${artifact_directory}"
- COMMAND
- ${compiler_to_run} -c ${cxx_flags}
- "${target_compile_flags_joined_genex}"
- "${target_defines_joined_genex}"
- ${hcleanFLAGS}
- "${target_includes_joined_genex}"
- ${framework_includes}
- ${hcleanDEFS}
- -xc++ "${input_path}"
- -o${artifact_path}
- IMPLICIT_DEPENDS CXX
- VERBATIM
- COMMAND_EXPAND_LISTS
- DEPENDS "${input_path}"
- )
- list(APPEND hclean_artifacts "${artifact_path}")
- endforeach()
+ set(compiler_command_line
+ "${compiler_to_run}" "-c" "${cxx_flags}"
+ "${target_compile_flags_joined_genex}"
+ "${target_defines_joined_genex}"
+ "${hcleanFLAGS}"
+ "${target_includes_joined_genex}"
+ "${framework_includes}"
+ "${hcleanDEFS}"
+ )
+ string(JOIN " " compiler_command_line_variables
+ "-xc++"
+ "\${INPUT_HEADER_FILE}"
+ "-o"
+ "\${OUTPUT_ARTIFACT}"
+ )
+ set(input_header_path_type ABSOLUTE)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# -Za would enable strict standards behavior, but we can't add it because
# <windows.h> and <GL.h> violate the standards.
@@ -202,37 +188,87 @@ function(qt_internal_add_headersclean_target
# cl.exe needs a source path
get_filename_component(source_path "${QT_MKSPECS_DIR}/features/data/dummy.cpp" REALPATH)
- foreach(header ${hclean_headers})
- # We need realpath here to make sure path starts with drive letter
- get_filename_component(input_path "${header}" REALPATH)
- set(artifact_path "header_${header}.o")
- set(comment_header_path "${CMAKE_CURRENT_SOURCE_DIR}/${header}")
- file(RELATIVE_PATH comment_header_path "${PROJECT_SOURCE_DIR}" "${comment_header_path}")
-
- add_custom_command(
- OUTPUT "${artifact_path}"
- COMMENT "headersclean: Checking header ${comment_header_path}"
- COMMAND
- ${compiler_to_run} -nologo -c ${CMAKE_CXX_FLAGS}
- "${target_compile_flags_joined_genex}"
- "${target_defines_joined_genex}"
- ${hcleanFLAGS}
- "${target_includes_joined_genex}"
- ${hcleanDEFS}
- -FI "${input_path}"
- -Fo${artifact_path} "${source_path}"
- IMPLICIT_DEPENDS CXX
- VERBATIM
- COMMAND_EXPAND_LISTS
- DEPENDS "${input_path}"
- )
- list(APPEND hclean_artifacts "${artifact_path}")
- endforeach()
+ set(compiler_command_line
+ "${compiler_to_run}" "-nologo" "-c" "${CMAKE_CXX_FLAGS}"
+ "${target_compile_flags_joined_genex}"
+ "${target_defines_joined_genex}"
+ "${hcleanFLAGS}"
+ "${target_includes_joined_genex}"
+ "${hcleanDEFS}"
+ )
+ string(JOIN " " compiler_command_line_variables
+ "-FI"
+ "\${INPUT_HEADER_FILE}"
+ "-Fo\${OUTPUT_ARTIFACT}"
+ "${source_path}"
+ )
+
+ set(input_header_path_type REALPATH)
else()
message(FATAL_ERROR "CMAKE_CXX_COMPILER_ID \"${CMAKE_CXX_COMPILER_ID}\" is not supported"
" for the headersclean check.")
endif()
+ get_target_property(module_include_name ${target} _qt_module_include_name)
+
+ unset(header_check_exceptions)
+ if(QT_USE_SYNCQT_CPP)
+ set(header_check_exceptions
+ "${CMAKE_CURRENT_BINARY_DIR}/${module_include_name}_header_check_exceptions")
+ endif()
+ set(headers_check_parameters
+ "${CMAKE_CURRENT_BINARY_DIR}/${module_target}HeadersCheckParameters${config_suffix}.cmake")
+ string(JOIN "\n" headers_check_parameters_content
+ "set(HEADER_CHECK_EXCEPTIONS"
+ " \"${header_check_exceptions}\")"
+ "set(HEADER_CHECK_COMPILER_COMMAND_LINE"
+ " \[\[$<JOIN:${compiler_command_line},\]\]\n \[\[>\]\]\n"
+ " ${compiler_command_line_variables}"
+ ")"
+ )
+ file(GENERATE OUTPUT "${headers_check_parameters}"
+ CONTENT "${headers_check_parameters_content}")
+
+ set(sync_headers_dep "")
+ if(QT_USE_SYNCQT_CPP)
+ set(sync_headers_dep "sync_headers")
+ endif()
+
+ foreach(header ${hclean_headers})
+ # We need realpath here to make sure path starts with drive letter
+ get_filename_component(input_path "${header}" ${input_header_path_type})
+
+ get_filename_component(input_file_name ${input_path} NAME)
+ set(artifact_path "${CMAKE_CURRENT_BINARY_DIR}/header_check/${input_file_name}.o")
+
+ if(input_path MATCHES "${CMAKE_BINARY_DIR}")
+ set(input_base_dir "${CMAKE_BINARY_DIR}")
+ elseif(input_path MATCHES "${CMAKE_SOURCE_DIR}")
+ set(input_base_dir "${CMAKE_SOURCE_DIR}")
+ endif()
+ file(RELATIVE_PATH comment_header_path "${input_base_dir}" "${input_path}")
+
+ add_custom_command(
+ OUTPUT "${artifact_path}"
+ COMMENT "headersclean: Checking header ${comment_header_path}"
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/header_check"
+ COMMAND ${CMAKE_COMMAND}
+ -DINPUT_HEADER_FILE=${input_path}
+ -DOUTPUT_ARTIFACT=${artifact_path}
+ -DPARAMETERS=${headers_check_parameters}
+ -P "${QT_CMAKE_DIR}/QtModuleHeadersCheck.cmake"
+ IMPLICIT_DEPENDS CXX
+ VERBATIM
+ COMMAND_EXPAND_LISTS
+ DEPENDS
+ ${headers_check_parameters}
+ ${sync_headers_dep}
+ ${input_path}
+ ${header_check_exceptions}
+ )
+ list(APPEND hclean_artifacts "${artifact_path}")
+ endforeach()
+
add_custom_target(${module_target}_headersclean_check
COMMENT "headersclean: Checking headers in ${module_include_name}"
DEPENDS ${hclean_artifacts}
diff --git a/cmake/QtModuleHeadersCheck.cmake b/cmake/QtModuleHeadersCheck.cmake
new file mode 100644
index 0000000000..d241f5bb55
--- /dev/null
+++ b/cmake/QtModuleHeadersCheck.cmake
@@ -0,0 +1,33 @@
+cmake_minimum_required(VERSION 3.16)
+# The PARAMETERS file should specify the following variables for the correct work of
+# this script:
+# HEADER_CHECK_EXCEPTIONS - path to file that contains exceptions.
+# The file is created by syncqt.
+#
+# HEADER_CHECK_COMPILER_COMMAND_LINE - compiler command line
+include("${PARAMETERS}")
+
+if(EXISTS ${HEADER_CHECK_EXCEPTIONS})
+ file(READ ${HEADER_CHECK_EXCEPTIONS} header_check_exception_list)
+endif()
+
+file(TO_CMAKE_PATH "${INPUT_HEADER_FILE}" header)
+foreach(exception IN LISTS header_check_exception_list)
+ file(TO_CMAKE_PATH "${exception}" exception)
+ if(exception STREQUAL header)
+ file(WRITE "${OUTPUT_ARTIFACT}" "skipped")
+ return()
+ endif()
+endforeach()
+
+execute_process(COMMAND ${HEADER_CHECK_COMPILER_COMMAND_LINE}
+ RESULT_VARIABLE result
+ OUTPUT_VARIABLE output
+ ERROR_VARIABLE output
+)
+
+if(NOT result EQUAL 0)
+ message(FATAL_ERROR "${INPUT_HEADER_FILE} header check"
+ " failed: ${HEADER_CHECK_COMPILER_COMMAND_LINE}\n"
+ " ${output}")
+endif()
diff --git a/cmake/QtModuleHelpers.cmake b/cmake/QtModuleHelpers.cmake
index cf64e485e8..a5ff37d530 100644
--- a/cmake/QtModuleHelpers.cmake
+++ b/cmake/QtModuleHelpers.cmake
@@ -29,6 +29,7 @@ macro(qt_internal_get_internal_add_module_keywords option_args single_args multi
EXTERNAL_HEADERS_DIR
PRIVATE_HEADER_FILTERS
QPA_HEADER_FILTERS
+ HEADER_SYNC_SOURCE_DIRECTORY
${__default_target_info_args}
)
set(${multi_args}
@@ -111,6 +112,12 @@ endfunction()
# QPA_HEADER_FILTERS
# The regular expressions that filter QPA header files out of target sources.
# The value must use the following format 'regex1|regex2|regex3'.
+#
+# HEADER_SYNC_SOURCE_DIRECTORY
+# The source directory for header sync procedure. Header files outside this directory will be
+# ignored by syncqt. The specifying this directory allows to skip the parsing of the whole
+# CMAKE_CURRENT_SOURCE_DIR for the header files that needs to be synced and only parse the
+# single subdirectory, that meanwhile can be outside the CMAKE_CURRENT_SOURCE_DIR tree.
function(qt_internal_add_module target)
qt_internal_get_internal_add_module_keywords(
module_option_args
@@ -382,13 +389,18 @@ function(qt_internal_add_module target)
else()
set_property(TARGET ${target} APPEND PROPERTY EXPORT_PROPERTIES _qt_module_include_name)
set_target_properties("${target}" PROPERTIES
- _qt_module_include_name "${module_include_name}")
+ _qt_module_include_name "${module_include_name}"
+ _qt_module_has_headers ON
+ )
- # Use QT_BUILD_DIR for the syncqt call.
- # So we either write the generated files into the qtbase non-prefix build root, or the
- # module specific build root.
+ # Need to call qt_ensure_sync_qt to install syncqt.pl script.
qt_ensure_sync_qt()
- set(syncqt_full_command "${HOST_PERL}" -w "${QT_SYNCQT}"
+ # Repo uses old perl script to sync files.
+ if(NOT QT_USE_SYNCQT_CPP)
+ # Use QT_BUILD_DIR for the syncqt call.
+ # So we either write the generated files into the qtbase non-prefix build root, or the
+ # module specific build root.
+ set(syncqt_full_command "${HOST_PERL}" -w "${QT_SYNCQT}"
-quiet
-check-includes
-module "${module_include_name}"
@@ -396,18 +408,24 @@ function(qt_internal_add_module target)
-outdir "${QT_BUILD_DIR}"
-builddir "${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}")
- message(STATUS "Running syncqt for module: '${module_include_name}' ")
- execute_process(COMMAND ${syncqt_full_command} RESULT_VARIABLE syncqt_ret)
- if(NOT syncqt_ret EQUAL 0)
- message(FATAL_ERROR "Failed to run syncqt, return code: ${syncqt_ret}")
- endif()
-
- set_target_properties("${target}" PROPERTIES
- _qt_module_has_headers ON)
-
- ### FIXME: Can we replace headers.pri?
- qt_read_headers_pri("${module_build_interface_include_dir}" "module_headers")
+ message(STATUS "Running syncqt for module: '${module_include_name}' ")
+ execute_process(COMMAND ${syncqt_full_command} RESULT_VARIABLE syncqt_ret)
+ if(NOT syncqt_ret EQUAL 0)
+ message(FATAL_ERROR "Failed to run syncqt, return code: ${syncqt_ret}")
+ endif()
+ ### FIXME: Can we replace headers.pri?
+ qt_read_headers_pri("${module_build_interface_include_dir}" "module_headers")
+ set_property(TARGET ${target} APPEND PROPERTY
+ _qt_module_timestamp_dependencies "${module_headers_generated}")
+ else()
+ set(sync_source_directory "${CMAKE_CURRENT_SOURCE_DIR}")
+ if(arg_HEADER_SYNC_SOURCE_DIRECTORY)
+ set(sync_source_directory "${arg_HEADER_SYNC_SOURCE_DIRECTORY}")
+ endif()
+ set_target_properties(${target} PROPERTIES
+ _qt_sync_source_directory "${sync_source_directory}")
+ endif()
# We should not generate export headers if module is defined as pure STATIC.
# Static libraries don't need to export their symbols, and corner cases when sources are
# also used in shared libraries, should be handled manually.
@@ -428,6 +446,9 @@ function(qt_internal_add_module target)
set(module_depends_header
"${module_build_interface_include_dir}/${module_include_name}Depends")
+ set_source_files_properties("${module_depends_header}" PROPERTIES GENERATED TRUE)
+ set_target_properties(${target} PROPERTIES _qt_module_depends_header
+ "${module_depends_header}")
if(NOT ${arg_HEADER_MODULE})
set(module_header "${module_build_interface_include_dir}/${module_include_name}")
set_property(TARGET "${target}" PROPERTY MODULE_HEADER
@@ -455,15 +476,17 @@ function(qt_internal_add_module target)
DESTINATION "${module_install_interface_include_dir}"
)
else()
- if(arg_EXTERNAL_HEADERS)
- set(module_headers_public "${arg_EXTERNAL_HEADERS}")
+ if(NOT QT_USE_SYNCQT_CPP)
+ if(arg_EXTERNAL_HEADERS)
+ set(module_headers_public "${arg_EXTERNAL_HEADERS}")
+ endif()
+ qt_internal_install_module_headers(${target}
+ PUBLIC
+ ${module_headers_public}
+ "${module_depends_header}"
+ "${module_header}"
+ )
endif()
- qt_internal_install_module_headers(${target}
- PUBLIC
- ${module_headers_public}
- "${module_depends_header}"
- "${module_header}"
- )
endif()
endif()
@@ -650,7 +673,7 @@ function(qt_internal_add_module target)
)
endif()
- if(NOT arg_HEADER_MODULE)
+ if(NOT arg_HEADER_MODULE AND NOT QT_USE_SYNCQT_CPP)
if(DEFINED module_headers_private)
qt_internal_add_linker_version_script("${target}" PRIVATE_HEADERS ${module_headers_private} ${module_headers_qpa})
else()
@@ -671,7 +694,7 @@ function(qt_internal_add_module target)
string(APPEND final_injections "${extra_library_injections} ")
endif()
- if(final_injections)
+ if(final_injections AND NOT QT_USE_SYNCQT_CPP)
qt_install_injections(${target} "${QT_BUILD_DIR}" "${QT_INSTALL_DIR}" ${final_injections})
endif()
@@ -853,10 +876,9 @@ set(QT_LIBINFIX \"${QT_LIBINFIX}\")")
endif()
endif()
- if(QT_FEATURE_headersclean AND NOT arg_NO_MODULE_HEADERS)
+ if(QT_FEATURE_headersclean AND NOT arg_NO_MODULE_HEADERS AND NOT QT_USE_SYNCQT_CPP)
qt_internal_add_headersclean_target(
${target}
- "${module_include_name}"
"${module_headers_clean}")
endif()
@@ -890,16 +912,29 @@ endfunction()
function(qt_finalize_module target)
qt_internal_collect_module_headers(module_headers ${target})
- set_property(TARGET ${target} APPEND PROPERTY
- _qt_module_timestamp_dependencies "${module_headers_public}")
# qt_internal_install_module_headers needs to be called before
# qt_finalize_framework_headers_copy, because the last uses the QT_COPIED_FRAMEWORK_HEADERS
- # property which supposed to be updated inside every qt_internal_install_module_headers call.
- qt_internal_install_module_headers(${target}
- PRIVATE ${module_headers_private}
- QPA ${module_headers_qpa}
- )
+ # property which supposed to be updated inside every qt_internal_install_module_headers
+ # call.
+ if(QT_USE_SYNCQT_CPP)
+ if(QT_FEATURE_headersclean)
+ qt_internal_add_headersclean_target(${target} "${module_headers_public}")
+ endif()
+ qt_internal_target_sync_headers(${target} "${module_headers_all}"
+ "${module_headers_generated}")
+ get_target_property(module_depends_header ${target} _qt_module_depends_header)
+ qt_internal_install_module_headers(${target}
+ PUBLIC ${module_headers_public} "${module_depends_header}"
+ PRIVATE ${module_headers_private}
+ QPA ${module_headers_qpa}
+ )
+ else()
+ qt_internal_install_module_headers(${target}
+ PRIVATE ${module_headers_private}
+ QPA ${module_headers_qpa}
+ )
+ endif()
qt_finalize_framework_headers_copy(${target})
qt_generate_prl_file(${target} "${INSTALL_LIBDIR}")
@@ -1098,6 +1133,13 @@ function(qt_internal_generate_cpp_global_exports target module_define_infix)
set(${out_public_header} "${generated_header_path}" PARENT_SCOPE)
target_sources(${target} PRIVATE "${generated_header_path}")
+ set_source_files_properties("${generated_header_path}" PROPERTIES GENERATED TRUE)
+ if(NOT QT_USE_SYNCQT_CPP)
+ qt_internal_install_module_headers(${target}
+ PUBLIC
+ "${generated_header_path}"
+ )
+ endif()
if(arg_GENERATE_PRIVATE_CPP_EXPORTS)
set(generated_private_header_path
@@ -1110,35 +1152,7 @@ function(qt_internal_generate_cpp_global_exports target module_define_infix)
set(${out_private_header} "${generated_private_header_path}" PARENT_SCOPE)
target_sources(${target} PRIVATE "${generated_private_header_path}")
- endif()
-
- get_target_property(is_framework ${target} FRAMEWORK)
-
- get_target_property(target_type ${target} TYPE)
- set(is_interface_lib 0)
- if(target_type STREQUAL "INTERFACE_LIBRARY")
- set(is_interface_lib 1)
- endif()
-
- set_property(TARGET ${target} APPEND PROPERTY
- _qt_module_timestamp_dependencies "${generated_header_path}")
-
- if(is_framework)
- if(NOT is_interface_lib)
- qt_copy_framework_headers(${target} PUBLIC "${generated_header_path}")
-
- if(arg_GENERATE_PRIVATE_CPP_EXPORTS)
- qt_copy_framework_headers(${target} PRIVATE "${generated_private_header_path}")
- endif()
- endif()
- else()
- qt_install(FILES "${generated_header_path}"
- DESTINATION "${module_install_interface_include_dir}")
-
- if(arg_GENERATE_PRIVATE_CPP_EXPORTS)
- qt_install(FILES "${generated_private_header_path}"
- DESTINATION "${module_install_interface_private_include_dir}")
- endif()
+ set_source_files_properties("${generated_private_header_path}" PROPERTIES GENERATED TRUE)
endif()
endfunction()
@@ -1180,10 +1194,9 @@ function(qt_internal_install_module_headers target)
qt_install(FILES ${arg_PRIVATE}
DESTINATION "${module_install_interface_private_include_dir}")
endif()
- endif()
-
- if(arg_QPA)
- qt_install(FILES ${arg_QPA} DESTINATION "${module_install_interface_qpa_include_dir}")
+ if(arg_QPA)
+ qt_install(FILES ${arg_QPA} DESTINATION "${module_install_interface_qpa_include_dir}")
+ endif()
endif()
endfunction()
@@ -1191,6 +1204,7 @@ function(qt_internal_collect_module_headers out_var target)
set(${out_var}_public "")
set(${out_var}_private "")
set(${out_var}_qpa "")
+ set(${out_var}_all "")
qt_internal_get_target_sources(sources ${target})
@@ -1203,6 +1217,7 @@ function(qt_internal_collect_module_headers out_var target)
if(NOT file_name MATCHES ".+\\.h$")
continue()
endif()
+ get_source_file_property(is_generated "${file_path}" GENERATED)
get_filename_component(file_path "${file_path}" ABSOLUTE)
get_filename_component(file_path "${file_path}" REALPATH)
list(APPEND ${out_var}_all "${file_path}")
@@ -1213,6 +1228,9 @@ function(qt_internal_collect_module_headers out_var target)
elseif(NOT public_filter OR file_name MATCHES "${public_filter}")
list(APPEND ${out_var}_public "${file_path}")
endif()
+ if(is_generated)
+ list(APPEND ${out_var}_generated "${file_path}")
+ endif()
endforeach()
set(header_types public private qpa)
@@ -1226,6 +1244,8 @@ function(qt_internal_collect_module_headers out_var target)
set(${out_var}_${header_type} "${${out_var}_${header_type}}" PARENT_SCOPE)
endforeach()
+ set(${out_var}_all "${${out_var}_all}" PARENT_SCOPE)
+ set(${out_var}_generated "${${out_var}_generated}" PARENT_SCOPE)
if(has_header_types_properties)
set_target_properties(${target} PROPERTIES ${has_header_types_properties})
diff --git a/cmake/QtPluginHelpers.cmake b/cmake/QtPluginHelpers.cmake
index 7b4f1ae669..aca5421221 100644
--- a/cmake/QtPluginHelpers.cmake
+++ b/cmake/QtPluginHelpers.cmake
@@ -276,6 +276,8 @@ function(qt_internal_add_plugin target)
)
endif()
endif()
+
+ qt_internal_add_autogen_sync_header_dependencies(${target} ${qt_module_target})
endif()
# Change the configuration file install location for qml plugins into the Qml package location.
diff --git a/cmake/QtPostProcessHelpers.cmake b/cmake/QtPostProcessHelpers.cmake
index 967413265c..0ca6bdbfa9 100644
--- a/cmake/QtPostProcessHelpers.cmake
+++ b/cmake/QtPostProcessHelpers.cmake
@@ -1,8 +1,9 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-function(qt_internal_write_depends_file target module_include_name)
- set(outfile "${QT_BUILD_DIR}/include/${module_include_name}/${module_include_name}Depends")
+function(qt_internal_write_depends_file target)
+ get_target_property(module_depends_header ${target} _qt_module_depends_header)
+ set(outfile "${module_depends_header}")
set(contents "/* This file was generated by cmake with the info from ${target} target. */\n")
string(APPEND contents "#ifdef __cplusplus /* create empty PCH in C mode */\n")
foreach (m ${ARGN})
@@ -249,8 +250,7 @@ function(qt_internal_create_module_depends_file target)
get_target_property(hasModuleHeaders "${target}" _qt_module_has_headers)
if (${hasModuleHeaders})
- get_target_property(module_include_name "${target}" _qt_module_include_name)
- qt_internal_write_depends_file(${target} ${module_include_name} ${qtdeps})
+ qt_internal_write_depends_file(${target} ${qtdeps})
endif()
if(third_party_deps OR main_module_tool_deps OR target_deps)
diff --git a/cmake/QtSyncQtHelpers.cmake b/cmake/QtSyncQtHelpers.cmake
index 40f9cf8e25..fa7e2bf066 100644
--- a/cmake/QtSyncQtHelpers.cmake
+++ b/cmake/QtSyncQtHelpers.cmake
@@ -206,3 +206,269 @@ function(qt_compute_injection_forwarding_header target)
string(APPEND ${arg_OUT_VAR} " ${relpath}:${fwd}")
set(${arg_OUT_VAR} ${${arg_OUT_VAR}} PARENT_SCOPE)
endfunction()
+
+# The function generates the Qt module header structure in build directory and creates install
+# rules. Apart the lists of header files the function takes into account
+# QT_REPO_PUBLIC_NAMESPACE_REGEX cache variable, that can be set by repository in .cmake.conf file.
+# The variable tells the syncqt program, what namespaces are treated as public. Symbols in public
+# namespaces are considered when generating CaMeL case header files.
+function(qt_internal_target_sync_headers target module_headers module_headers_generated)
+ if(NOT TARGET ${QT_CMAKE_EXPORT_NAMESPACE}::syncqt)
+ message(FATAL_ERROR "${QT_CMAKE_EXPORT_NAMESPACE}::syncqt is not a target.")
+ endif()
+ get_target_property(has_headers ${target} _qt_module_has_headers)
+ if(NOT has_headers)
+ return()
+ endif()
+
+ qt_internal_module_info(module "${target}")
+
+ get_target_property(sync_source_directory ${target} _qt_sync_source_directory)
+ set(syncqt_timestamp "${CMAKE_CURRENT_BINARY_DIR}/${target}_syncqt_timestamp")
+ set(syncqt_outputs "${syncqt_timestamp}")
+
+ set(is_interface_lib FALSE)
+ get_target_property(type ${target} TYPE)
+ if(type STREQUAL "INTERFACE_LIBRARY")
+ set(is_interface_lib TRUE)
+ endif()
+
+ set(version_script_private_content_file "")
+ if(NOT is_interface_lib)
+ list(APPEND syncqt_outputs
+ "${module_build_interface_include_dir}/${module}Version"
+ "${module_build_interface_include_dir}/qt${module_lower}version.h")
+ if(TEST_ld_version_script)
+ set(version_script_private_content_file
+ "${CMAKE_CURRENT_BINARY_DIR}/${target}.version.private_content")
+ set(version_script_args
+ "-versionScript" "${version_script_private_content_file}")
+ list(APPEND syncqt_outputs "${version_script_private_content_file}")
+ qt_internal_add_linker_version_script(${target}
+ PRIVATE_CONTENT_FILE "${version_script_private_content_file}")
+ endif()
+ endif()
+
+ # Check for _qt_module_is_3rdparty_header_library flag to detect non-Qt modules and
+ # indicate this to syncqt.
+ get_target_property(is_3rd_party_library ${target} _qt_module_is_3rdparty_header_library)
+ set(non_qt_module_argument "")
+ if(is_3rd_party_library)
+ set(non_qt_module_argument "-nonQt")
+ else()
+ list(APPEND syncqt_outputs "${module_build_interface_include_dir}/${module}")
+ if(QT_FEATURE_headersclean)
+ list(APPEND syncqt_outputs
+ "${CMAKE_CURRENT_BINARY_DIR}/${module}_header_check_exceptions")
+ endif()
+ endif()
+
+ set(is_framework FALSE)
+ if(NOT is_interface_lib)
+ get_target_property(is_framework ${target} FRAMEWORK)
+ if(is_framework)
+ qt_internal_get_framework_info(fw ${target})
+ get_target_property(fw_output_base_dir ${target} LIBRARY_OUTPUT_DIRECTORY)
+ set(framework_args "-framework"
+ "-frameworkIncludeDir" "${fw_output_base_dir}/${fw_versioned_header_dir}"
+ )
+ endif()
+ endif()
+
+ qt_internal_get_qt_all_known_modules(known_modules)
+
+ get_target_property(is_internal_module ${target} _qt_is_internal_module)
+ set(internal_module_argument "")
+ if(is_internal_module)
+ set(internal_module_argument "-internal")
+ endif()
+
+ get_target_property(qpa_filter_regex ${target} _qt_module_qpa_headers_filter_regex)
+ get_target_property(private_filter_regex ${target} _qt_module_private_headers_filter_regex)
+
+ # We need to use the real paths since otherwise it may lead to the invalid work of the
+ # std::filesystem API
+ get_filename_component(source_dir_real "${sync_source_directory}" REALPATH)
+ get_filename_component(binary_dir_real "${CMAKE_CURRENT_BINARY_DIR}" REALPATH)
+
+ if(QT_REPO_PUBLIC_NAMESPACE_REGEX)
+ set(public_namespaces_filter -publicNamespaceFilter "${QT_REPO_PUBLIC_NAMESPACE_REGEX}")
+ endif()
+
+ if(qpa_filter_regex)
+ set(qpa_filter_argument
+ -qpaHeadersFilter "${qpa_filter_regex}"
+ )
+ endif()
+
+ set(common_syncqt_arguments
+ -module "${module}"
+ -sourceDir "${source_dir_real}"
+ -binaryDir "${binary_dir_real}"
+ -privateHeadersFilter "${private_filter_regex}"
+ -includeDir "${module_build_interface_include_dir}"
+ -privateIncludeDir "${module_build_interface_private_include_dir}"
+ -qpaIncludeDir "${module_build_interface_qpa_include_dir}"
+ ${qpa_filter_argument}
+ ${public_namespaces_filter}
+ ${non_qt_module_argument}
+ ${internal_module_argument}
+ )
+
+ if(QT_INTERNAL_ENABLE_SYNCQT_DEBUG_OUTPUT)
+ list(APPEND common_syncqt_arguments -debug)
+ endif()
+
+
+ if(is_framework)
+ list(REMOVE_ITEM module_headers "${CMAKE_CURRENT_BINARY_DIR}/${target}_fake_header.h")
+ endif()
+
+ # Filter the generated ui_ header files and header files located in the 'doc/' subdirectory.
+ list(FILTER module_headers EXCLUDE REGEX
+ "(.+/(ui_)[^/]+\\.h|${CMAKE_CURRENT_SOURCE_DIR}(/.+)?/doc/+\\.h)")
+
+ set(module_headers_rsp "${binary_dir_real}/module_headers")
+ list(JOIN module_headers "\n" module_headers_string)
+ qt_configure_file_v2(OUTPUT "${module_headers_rsp}" CONTENT "${module_headers_string}")
+
+ set(module_headers_generated_rsp "${binary_dir_real}/module_headers_generated")
+ list(JOIN module_headers_generated "\n" module_headers_generated_string)
+ qt_configure_file_v2(OUTPUT "${module_headers_generated_rsp}" CONTENT
+ "${module_headers_generated_string}")
+
+ set(syncqt_staging_dir "${module_build_interface_include_dir}/.syncqt_staging")
+ add_custom_command(
+ OUTPUT
+ ${syncqt_outputs}
+ COMMAND
+ ${QT_CMAKE_EXPORT_NAMESPACE}::syncqt
+ ${common_syncqt_arguments}
+ -headers "@${module_headers_rsp}"
+ -generatedHeaders "@${module_headers_generated_rsp}"
+ -stagingDir "${syncqt_staging_dir}"
+ -knownModules ${known_modules}
+ ${framework_args}
+ ${version_script_args}
+ COMMAND
+ ${CMAKE_COMMAND} -E touch "${syncqt_timestamp}"
+ DEPENDS
+ ${module_headers_rsp}
+ ${module_headers_generated_rsp}
+ ${module_headers}
+ ${QT_CMAKE_EXPORT_NAMESPACE}::syncqt
+ COMMENT
+ "Running syncqt.cpp for module: ${module}"
+ VERBATIM
+ )
+ add_custom_target(${target}_sync_headers
+ DEPENDS
+ ${syncqt_outputs}
+ )
+ add_dependencies(sync_headers ${target}_sync_headers)
+
+ # This target is required when building docs, to make all header files and their aliases
+ # available for qdoc.
+ # ${target}_sync_headers is added as dependency to make sure that
+ # ${target}_sync_all_public_headers is running after ${target}_sync_headers, when building docs.
+ add_custom_target(${target}_sync_all_public_headers
+ COMMAND
+ ${QT_CMAKE_EXPORT_NAMESPACE}::syncqt
+ ${common_syncqt_arguments}
+ -all
+ DEPENDS
+ ${module_headers}
+ ${QT_CMAKE_EXPORT_NAMESPACE}::syncqt
+ ${target}_sync_headers
+ VERBATIM
+ )
+
+ if(NOT TARGET sync_all_public_headers)
+ add_custom_target(sync_all_public_headers)
+ endif()
+ add_dependencies(sync_all_public_headers ${target}_sync_all_public_headers)
+
+ if(NOT is_3rd_party_library AND NOT is_framework)
+ # Install all the CaMeL style aliases of header files from the staging directory in one rule
+ qt_install(DIRECTORY "${syncqt_staging_dir}/"
+ DESTINATION "${module_install_interface_include_dir}"
+ )
+ endif()
+
+ if(NOT is_interface_lib)
+ set_property(TARGET ${target}
+ APPEND PROPERTY AUTOGEN_TARGET_DEPENDS "${target}_sync_headers")
+ endif()
+ add_dependencies(${target} "${target}_sync_headers")
+
+
+ get_target_property(private_module_target ${target} _qt_private_module_target_name)
+ if(private_module_target)
+ add_dependencies(${private_module_target} "${target}_sync_headers")
+ endif()
+
+ # Run sync Qt first time at configure step to make all header files available for the code model
+ # of IDEs.
+ get_property(synced_modules GLOBAL PROPERTY _qt_synced_modules)
+ if(NOT "${module}" IN_LIST synced_modules)
+ message(STATUS "Running syncqt.cpp for module: ${module}")
+ get_target_property(syncqt_location ${QT_CMAKE_EXPORT_NAMESPACE}::syncqt LOCATION)
+ execute_process(
+ COMMAND
+ ${syncqt_location}
+ ${common_syncqt_arguments}
+ -headers "@${module_headers_rsp}"
+ -generatedHeaders "@${module_headers_generated_rsp}"
+ -stagingDir "${syncqt_staging_dir}"
+ -knownModules ${known_modules}
+ ${framework_args}
+ RESULT_VARIABLE syncqt_result
+ OUTPUT_VARIABLE syncqt_output
+ ERROR_VARIABLE syncqt_output
+ )
+ if(NOT syncqt_result EQUAL 0)
+ message(FATAL_ERROR
+ "Unable to execute syncqt.cpp for module ${target}: ${syncqt_output}")
+ endif()
+ set_property(GLOBAL APPEND PROPERTY _qt_synced_modules ${module})
+ endif()
+endfunction()
+
+function(qt_internal_collect_sync_header_dependencies out_var skip_non_existing)
+ if(NOT QT_USE_SYNCQT_CPP)
+ set(${out_var} "" PARENT_SCOPE)
+ return()
+ endif()
+
+ list(LENGTH ARGN sync_headers_target_count)
+ if(sync_headers_target_count EQUAL 0)
+ message(FATAL_ERROR "Invalid use of qt_internal_collect_sync_header_dependencies,"
+ " dependencies are not specified")
+ endif()
+
+ set(${out_var} "")
+ foreach(sync_headers_target IN LISTS ARGN)
+ set(sync_headers_target "${sync_headers_target}_sync_headers")
+ if(NOT skip_non_existing OR TARGET ${sync_headers_target})
+ list(APPEND ${out_var} ${sync_headers_target})
+ endif()
+ endforeach()
+ list(REMOVE_DUPLICATES ${out_var})
+
+ set(${out_var} "${${out_var}}" PARENT_SCOPE)
+endfunction()
+
+function(qt_internal_add_sync_header_dependencies target)
+ qt_internal_collect_sync_header_dependencies(sync_headers_targets FALSE ${ARGN})
+ if(sync_headers_targets)
+ add_dependencies(${target} ${sync_headers_targets})
+ endif()
+endfunction()
+
+function(qt_internal_add_autogen_sync_header_dependencies target)
+ qt_internal_collect_sync_header_dependencies(sync_headers_targets TRUE ${ARGN})
+ foreach(sync_headers_target IN LISTS sync_headers_targets)
+ set_property(TARGET ${target} APPEND PROPERTY AUTOGEN_TARGET_DEPENDS
+ "${sync_headers_target}")
+ endforeach()
+endfunction()
diff --git a/cmake/QtToolHelpers.cmake b/cmake/QtToolHelpers.cmake
index 4184610ecd..8af9e50d68 100644
--- a/cmake/QtToolHelpers.cmake
+++ b/cmake/QtToolHelpers.cmake
@@ -634,10 +634,11 @@ function(qt_internal_add_configure_time_tool target_name)
set(extra_args "INSTALL_DIRECTORY" "${install_dir}")
endif()
+ string(REPLACE "\\\;" "\\\\\\\;" unparsed_arguments "${arg_UNPARSED_ARGUMENTS}")
qt_internal_add_configure_time_executable(${target_name}
OUTPUT_NAME ${name}
${extra_args}
- ${arg_UNPARSED_ARGUMENTS}
+ ${unparsed_arguments}
)
if(NOT arg_NO_INSTALL AND arg_TOOLS_TARGET)