Files
scylladb/cmake/check_headers.cmake
Kefu Chai 6645cdf3b6 build: cmake: improve source generated for check_header
in check_headers.cmake, we verify the self containness of a header file
by replicating it and remove `#pragma once` directive in this header.
but this approach failed to compile headers which include a header file
with the same name in the root source directory, as we add
`-I<directory-of-original-header>` in the cflags when building the
generated source file, so that it can include the headers in the same
directory. but this confuses the compiler, as, assuming we have "log.hh"
in current directory, and under the root source directory, the compiler
would always include the "log.hh" in the current directory even it
should have included "log.hh" under the root source directory.

in this change, instead of adding `-I<directory-of-original-header>` to
cflags, we just include the header under test in a new .cc file
solely generated for testing. this should address this problem.

Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>

Closes scylladb/scylladb#21216
2024-10-22 06:28:16 +03:00

107 lines
3.4 KiB
CMake

function (check_headers check_headers_target target)
if(NOT TARGET ${check_headers_target})
return()
endif()
# "target" is required, so we can get the compiling options for compiling
# the headers.
cmake_parse_arguments (
parsed_args
""
"GLOB;GLOB_RECURSE;EXCLUDE;INCLUDE"
""
${ARGN})
set(sources "")
if(DEFINED parsed_args_GLOB)
file(GLOB sources
LIST_DIRECTORIES false
RELATIVE ${CMAKE_SOURCE_DIR}
"${parsed_args_GLOB}")
elseif(DEFINED parsed_args_GLOB_RECURSE)
file(GLOB_RECURSE sources
LIST_DIRECTORIES false
RELATIVE ${CMAKE_SOURCE_DIR}
"${parsed_args_GLOB_RECURSE}")
else()
message(FATAL_ERROR "Please specify GLOB or GLOB_RECURSE.")
endif()
if(DEFINED parsed_args_INCLUDE)
list(FILTER sources INCLUDE REGEX "${parsed_args_INCLUDE}")
endif()
if(DEFINED parsed_args_EXCLUDE)
list(FILTER sources EXCLUDE REGEX "${parsed_args_EXCLUDE}")
endif()
foreach(fn ${sources})
get_filename_component(file_dir ${fn} DIRECTORY)
set(src_dir "${CMAKE_BINARY_DIR}/check-headers/${file_dir}")
file(MAKE_DIRECTORY "${src_dir}")
get_filename_component(file_name ${fn} NAME)
set(src "${src_dir}/${file_name}.cc")
# CMake refuses to compile .hh files, so we need to rename them first.
add_custom_command(
OUTPUT ${src}
DEPENDS ${CMAKE_SOURCE_DIR}/${fn}
COMMAND ${CMAKE_COMMAND} -E echo "#include \"${fn}\"" > "${src}"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
VERBATIM)
list(APPEND srcs "${src}")
endforeach()
if(NOT srcs)
# not headers to checks
return()
endif()
set(check_lib "${check_headers_target}-${target}")
add_library(${check_lib} EXCLUDE_FROM_ALL)
target_sources(${check_lib}
PRIVATE ${srcs})
# use ${target} as an interface library by consuming all of its
# compile time options
get_target_property(libraries ${target} LINK_LIBRARIES)
if (libraries)
# as a side effect, the libraries linked by the ${target} are also built as
# the dependencies of ${check_lib}. some of the libraries are scylla
# libraries. we could tell them from the 3rd party libraries, but we would
# have to recursively pull in their compiling options. so, since that
# we always build "check-headers" target along with "scylla", this does
# not incur overhead.
target_link_libraries(${check_lib}
PRIVATE ${libraries})
endif()
# if header includes other header files with relative path,
# we should satisfy it.
list(REMOVE_DUPLICATES includes)
target_include_directories(${check_lib}
PRIVATE ${includes})
get_target_property(includes ${target} INCLUDE_DIRECTORIES)
if(includes)
target_include_directories(${check_lib}
PRIVATE ${includes})
endif()
get_target_property(compile_options ${target} COMPILE_OPTIONS)
if(compile_options)
target_compile_options(${check_lib}
PRIVATE ${compile_options})
endif ()
# symbols in header file should always be referenced, but these
# are just pure headers, so unused variables should be tolerated.
target_compile_options(${check_lib}
PRIVATE
-Wno-unused-const-variable
-Wno-unused-function
-Wno-unused-variable)
get_target_property(compile_definitions ${target} COMPILE_DEFINITIONS)
if(compile_definitions)
target_compile_definitions(${check_lib}
PRIVATE ${compile_definitions})
endif()
add_dependencies(${check_headers_target} ${check_lib})
endfunction ()