diff options
Diffstat (limited to 'cmake/QtHeadersClean.cmake')
-rw-r--r-- | cmake/QtHeadersClean.cmake | 251 |
1 files changed, 150 insertions, 101 deletions
diff --git a/cmake/QtHeadersClean.cmake b/cmake/QtHeadersClean.cmake index c8b43ddfe8..938b6a7b41 100644 --- a/cmake/QtHeadersClean.cmake +++ b/cmake/QtHeadersClean.cmake @@ -1,26 +1,25 @@ -# Add a custom ${module_include_name}_header_check target that builds each header in +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# 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_headers_clean_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) + if(INPUT_headersclean AND WASM) + message(FATAL_ERROR "The headersclean targets are not supported on WASM platform.") + endif() + + get_target_property(no_headersclean_check ${module_target} _qt_no_headersclean_check) + if(no_headersclean_check) + 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 @@ -28,6 +27,7 @@ function(qt_internal_add_headers_clean_target -DQT_NO_CAST_FROM_ASCII -DQT_NO_URL_CAST_FROM_STRING -DQT_NO_CAST_FROM_BYTEARRAY + -DQT_NO_CONTEXTLESS_CONNECT -DQT_NO_KEYWORDS -DQT_TYPESAFE_FLAGS -DQT_USE_QSTRINGBUILDER @@ -39,7 +39,7 @@ function(qt_internal_add_headers_clean_target endif() set(prop_prefix "") - get_target_property(target_type "${target}" TYPE) + get_target_property(target_type "${module_target}" TYPE) if(target_type STREQUAL "INTERFACE_LIBRARY") set(prop_prefix "INTERFACE_") endif() @@ -50,6 +50,12 @@ function(qt_internal_add_headers_clean_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 @@ -99,40 +105,33 @@ function(qt_internal_add_headers_clean_target if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang|IntelLLVM") - # Turn on some extra warnings not found in -Wall -Wextra. - set(hcleanFLAGS -Wall -Wextra -Werror -Woverloaded-virtual -Wshadow -Wundef -Wfloat-equal - -Wnon-virtual-dtor -Wpointer-arith -Wformat-security -Wno-long-long -Wno-variadic-macros - -pedantic-errors) + # Compile header in strict C++20 mode. Enable further warnings. + set(hcleanFLAGS -std=c++2a + -Wall -Wextra -Werror -pedantic-errors + -Woverloaded-virtual -Wshadow -Wundef -Wfloat-equal + -Wnon-virtual-dtor -Wpointer-arith -Wformat-security + -Wchar-subscripts -Wold-style-cast + -Wredundant-decls # QTBUG-115583 + -fno-operator-names) if(QT_FEATURE_reduce_relocations AND UNIX) list(APPEND hcleanFLAGS -fPIC) endif() - # options accepted by GCC and Clang - list(APPEND hcleanFLAGS -Wchar-subscripts -Wold-style-cast) - if (NOT ((TEST_architecture_arch STREQUAL arm) OR (TEST_architecture_arch STREQUAL mips))) list(APPEND hcleanFLAGS -Wcast-align) endif() if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - list(APPEND hcleanFLAGS -Wzero-as-null-pointer-constant) - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.5) - list(APPEND hcleanFLAGS -Wdouble-promotion) - endif() - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.9) - list(APPEND hcleanFLAGS -Wfloat-conversion) - - # GCC 9 has a lot of false positives relating to these - list(APPEND hcleanFlags -Wno-deprecated-copy -Wno-redundant-move - -Wno-format-overflow -Wno-init-list-lifetime) - endif() + list(APPEND hcleanFLAGS -Wzero-as-null-pointer-constant + -Wdouble-promotion -Wfloat-conversion) endif() - # Use strict mode C++20, with no GNU extensions (see -pedantic-errors above). - list(APPEND hcleanFLAGS -std=c++2a) + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang|IntelLLVM") + list(APPEND hcleanFLAGS -Wshorten-64-to-32) + endif() separate_arguments(cxx_flags NATIVE_COMMAND ${CMAKE_CXX_FLAGS}) @@ -149,10 +148,7 @@ function(qt_internal_add_headers_clean_target # If additional package prefixes are provided, we consider they can contain frameworks # as well. foreach(prefix IN LISTS _qt_additional_packages_prefix_paths) - if(prefix MATCHES "/lib/cmake$") # Cut CMake files path - string(APPEND prefix "/../..") - endif() - get_filename_component(prefix "${prefix}" ABSOLUTE) + __qt_internal_reverse_prefix_path_from_cmake_dir(path "${path}") set(libdir "${prefix}/${INSTALL_LIBDIR}") if(EXISTS "${libdir}") @@ -162,73 +158,126 @@ function(qt_internal_add_headers_clean_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) - 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. - set(hcleanFLAGS -std:c++latest -Zc:__cplusplus -WX -W3) + # Note we can't enable -Za, as it does not support certain key Microsoft SDK header files + # we use. Microsoft suggests to use /permissive- instead, which is implicity set by + # -std:c++latest. + set(hcleanFLAGS -std:c++latest -Zc:__cplusplus -WX -W4 -EHsc) + + # Because we now add `-DNOMINMAX` to `PlatformCommonInternal`. + set(hcleanUDEFS -UNOMINMAX) # 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) - 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}" + "${hcleanUDEFS}" + ) + string(JOIN " " compiler_command_line_variables + "-FI" + "\${INPUT_HEADER_FILE}" + "-Fo\${OUTPUT_ARTIFACT}" + "${source_path}" + ) + + set(input_header_path_type REALPATH) else() - message(ERROR "CMAKE_CXX_COMPILER_ID \"${CMAKE_CXX_COMPILER_ID}\" is not supported for the headersclean check.") + message(FATAL_ERROR "CMAKE_CXX_COMPILER_ID \"${CMAKE_CXX_COMPILER_ID}\" is not supported" + " for the headersclean check.") endif() - add_custom_target(${module_include_name}_header_check - COMMENT "headersclean: Checking headers in ${module_include_name}" + qt_internal_module_info(module ${module_target}) + + unset(header_check_exceptions) + set(header_check_exceptions + "${CMAKE_CURRENT_BINARY_DIR}/${module}_header_check_exceptions") + 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 "${module_target}_sync_headers") + + 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") + + unset(input_base_dir) + 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() + + if(input_base_dir AND IS_ABSOLUTE "${input_base_dir}" AND IS_ABSOLUTE "${input_path}") + file(RELATIVE_PATH comment_header_path "${input_base_dir}" "${input_path}") + else() + set(comment_header_path "${input_path}") + endif() + + 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}" DEPENDS ${hclean_artifacts} VERBATIM) - add_dependencies(${module_target} ${module_include_name}_header_check) + if(NOT TARGET headersclean_check) + add_custom_target(headersclean_check ALL) + endif() + + add_dependencies(headersclean_check ${module_target}_headersclean_check) endfunction() |