604af4d5bc
Use the module-spefic sync_headers target when specifying dependencies for the headersclean custom command. Pick-to: 6.5 Fixes: QTBUG-113643 Change-Id: I6eb6f4a8881b252f7732ecfa79a9893ace5ce19f Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
283 lines
12 KiB
CMake
283 lines
12 KiB
CMake
# 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_headersclean_target module_target module_headers)
|
|
get_target_property(no_headersclean_check ${module_target} _qt_no_headersclean_check)
|
|
if(no_headersclean_check)
|
|
return()
|
|
endif()
|
|
|
|
set(hclean_headers "")
|
|
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()
|
|
endforeach()
|
|
|
|
# Make sure that the header compiles with our strict options
|
|
set(hcleanDEFS -DQT_NO_CAST_TO_ASCII
|
|
-DQT_NO_CAST_FROM_ASCII
|
|
-DQT_NO_URL_CAST_FROM_STRING
|
|
-DQT_NO_CAST_FROM_BYTEARRAY
|
|
-DQT_NO_KEYWORDS
|
|
-DQT_TYPESAFE_FLAGS
|
|
-DQT_USE_QSTRINGBUILDER
|
|
-DQT_USE_FAST_OPERATOR_PLUS)
|
|
|
|
set(compiler_to_run "${CMAKE_CXX_COMPILER}")
|
|
if(CMAKE_CXX_COMPILER_LAUNCHER)
|
|
list(PREPEND compiler_to_run "${CMAKE_CXX_COMPILER_LAUNCHER}")
|
|
endif()
|
|
|
|
set(prop_prefix "")
|
|
get_target_property(target_type "${module_target}" TYPE)
|
|
if(target_type STREQUAL "INTERFACE_LIBRARY")
|
|
set(prop_prefix "INTERFACE_")
|
|
endif()
|
|
|
|
set(target_includes_genex
|
|
"$<TARGET_PROPERTY:${module_target},${prop_prefix}INCLUDE_DIRECTORIES>")
|
|
set(includes_exist_genex "$<BOOL:${target_includes_genex}>")
|
|
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
|
|
# genex-enhanced compile definitions. An example of that is in
|
|
# qttools/src/designer/src/uiplugin/CMakeLists.txt which ends up causing the following error
|
|
# message:
|
|
# CMake Error at qtbase/cmake/QtModuleHelpers.cmake:35 (add_library):
|
|
# INTERFACE_LIBRARY targets may only have whitelisted properties. The
|
|
# property "QT_PLUGIN_CLASS_NAME" is not allowed.
|
|
# Call Stack (most recent call first):
|
|
# src/designer/src/uiplugin/CMakeLists.txt:7 (qt_internal_add_module)
|
|
if(NOT target_type STREQUAL "INTERFACE_LIBRARY")
|
|
set(target_defines_genex
|
|
"$<TARGET_PROPERTY:${module_target},${prop_prefix}COMPILE_DEFINITIONS>")
|
|
set(defines_exist_genex "$<BOOL:${target_defines_genex}>")
|
|
set(target_defines_joined_genex
|
|
"$<${defines_exist_genex}:-D$<JOIN:${target_defines_genex},;-D>>")
|
|
endif()
|
|
|
|
# TODO: FIXME
|
|
# Passing COMPILE_OPTIONS can break add_custom_command() if the values contain genexes
|
|
# that add_custom_command does not support.
|
|
#
|
|
# Such a case happens on Linux where libraries linking against Threads::Threads bring in a
|
|
# '<$<COMPILE_LANG_AND_ID:CUDA,NVIDIA>' genex.
|
|
#
|
|
# If this is really required for headersclean (and qmake's headerclean implementation does
|
|
# actually pass all flags of the associated target), we'll have to replace the genex usage
|
|
# with an implementation that recursively processes a target's dependencies property
|
|
# to compile an expanded list of values for COMPILE_OPTIONS, and then filter out any genexes.
|
|
#
|
|
# This is similar to the proposed workaround at
|
|
# https://gitlab.kitware.com/cmake/cmake/-/issues/21074#note_814979
|
|
#
|
|
# See also https://gitlab.kitware.com/cmake/cmake/-/issues/21336
|
|
#
|
|
#set(target_compile_options_genex "$<TARGET_PROPERTY:${module_target},COMPILE_OPTIONS>")
|
|
#set(compile_options_exist_genex "$<BOOL:${target_compile_options_genex}>")
|
|
#set(target_compile_options_joined_genex
|
|
# "$<${compile_options_exist_genex}:$<JOIN:${target_compile_options_genex},;>>")
|
|
|
|
set(target_compile_flags_genex
|
|
"$<TARGET_PROPERTY:${module_target},${prop_prefix}COMPILE_FLAGS>")
|
|
set(compile_flags_exist_genex "$<BOOL:${target_compile_flags_genex}>")
|
|
set(target_compile_flags_joined_genex
|
|
"$<${compile_flags_exist_genex}:$<JOIN:${target_compile_flags_genex},;>>")
|
|
|
|
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
|
|
-fno-operator-names
|
|
-pedantic-errors)
|
|
|
|
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)
|
|
endif()
|
|
endif()
|
|
|
|
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang|IntelLLVM")
|
|
list(APPEND hcleanFLAGS -Wshorten-64-to-32)
|
|
endif()
|
|
|
|
# Use strict mode C++20, with no GNU extensions (see -pedantic-errors above).
|
|
list(APPEND hcleanFLAGS -std=c++2a)
|
|
|
|
separate_arguments(cxx_flags NATIVE_COMMAND ${CMAKE_CXX_FLAGS})
|
|
|
|
if(APPLE AND CMAKE_OSX_SYSROOT)
|
|
list(APPEND cxx_flags "${CMAKE_CXX_SYSROOT_FLAG}" "${CMAKE_OSX_SYSROOT}")
|
|
endif()
|
|
|
|
if(APPLE AND QT_FEATURE_framework)
|
|
# For some reason CMake doesn't generate -iframework flags from the INCLUDE_DIRECTORIES
|
|
# generator expression we provide, so pass it explicitly and hope for the best.
|
|
list(APPEND framework_includes
|
|
"-iframework" "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}/${INSTALL_LIBDIR}")
|
|
|
|
# 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)
|
|
|
|
set(libdir "${prefix}/${INSTALL_LIBDIR}")
|
|
if(EXISTS "${libdir}")
|
|
list(APPEND framework_includes
|
|
"-iframework" "${libdir}")
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
|
|
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")
|
|
set(hcleanFLAGS -std:c++latest -Zc:__cplusplus -Za -WX -W3 -EHsc)
|
|
|
|
# cl.exe needs a source path
|
|
get_filename_component(source_path "${QT_MKSPECS_DIR}/features/data/dummy.cpp" REALPATH)
|
|
|
|
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()
|
|
|
|
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)
|
|
|
|
if(NOT TARGET headersclean_check)
|
|
add_custom_target(headersclean_check ALL)
|
|
endif()
|
|
|
|
add_dependencies(headersclean_check ${module_target}_headersclean_check)
|
|
endfunction()
|