mirror of
https://github.com/mpusz/mp-units.git
synced 2025-06-25 01:01:33 +02:00
test: standalone public headers tests added
This commit is contained in:
128
cmake/TestHeaders.cmake
Normal file
128
cmake/TestHeaders.cmake
Normal file
@ -0,0 +1,128 @@
|
||||
# Copyright Louis Dionne 2013-2017
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
#
|
||||
# This CMake module provides a function generating a unit test to make sure
|
||||
# that every public header can be included on its own.
|
||||
#
|
||||
# When a C++ library or application has many header files, it can happen that
|
||||
# a header does not include all the other headers it depends on. When this is
|
||||
# the case, it can happen that including that header file on its own will
|
||||
# break the compilation. This CMake module generates a dummy executable
|
||||
# comprised of many .cpp files, each of which includes a header file that
|
||||
# is part of the public API. In other words, the executable is comprised
|
||||
# of .cpp files of the form:
|
||||
#
|
||||
# #include <the/public/header.hpp>
|
||||
#
|
||||
# and then exactly one `main` function. If this succeeds to compile, it means
|
||||
# that the header can be included on its own, which is what clients expect.
|
||||
# Otherwise, you have a problem. Since writing these dumb unit tests by hand
|
||||
# is tedious and repetitive, you can use this CMake module to automate this
|
||||
# task.
|
||||
|
||||
# add_header_test(<target> [EXCLUDE_FROM_ALL] [EXCLUDE excludes...] HEADERS headers...)
|
||||
#
|
||||
# Generates header-inclusion unit tests for all the specified headers.
|
||||
#
|
||||
# This function creates a target which builds a dummy executable including
|
||||
# each specified header file individually. If this target builds successfully,
|
||||
# it means that all the specified header files can be included individually.
|
||||
#
|
||||
# Parameters
|
||||
# ----------
|
||||
# <target>:
|
||||
# The name of the target to generate.
|
||||
#
|
||||
# HEADERS headers:
|
||||
# A list of header files to generate the inclusion tests for. All headers
|
||||
# in this list must be represented as relative paths from the root of the
|
||||
# include directory added to the compiler's header search path. In other
|
||||
# words, it should be possible to include all headers in this list as
|
||||
#
|
||||
# #include <${header}>
|
||||
#
|
||||
# For example, for a library with the following structure:
|
||||
#
|
||||
# project/
|
||||
# doc/
|
||||
# test/
|
||||
# ...
|
||||
# include/
|
||||
# boost/
|
||||
# hana.hpp
|
||||
# hana/
|
||||
# transform.hpp
|
||||
# tuple.hpp
|
||||
# pair.hpp
|
||||
# ...
|
||||
#
|
||||
# When building the unit tests for that library, we'll add `-I project/include'
|
||||
# to the compiler's arguments. The list of public headers should therefore contain
|
||||
#
|
||||
# boost/hana.hpp
|
||||
# boost/hana/transform.hpp
|
||||
# boost/hana/tuple.hpp
|
||||
# boost/hana/pair.hpp
|
||||
# ...
|
||||
#
|
||||
# Usually, all the 'public' header files of a library should be tested for
|
||||
# standalone inclusion. A header is considered 'public' if a client should
|
||||
# be able to include that header on its own.
|
||||
#
|
||||
# [EXCLUDE excludes]:
|
||||
# An optional list of headers or regexes for which no unit test should be
|
||||
# generated. Basically, any header in the list specified by the `HEADERS`
|
||||
# argument that matches anything in `EXCLUDE` will be skipped.
|
||||
#
|
||||
# [EXCLUDE_FROM_ALL]:
|
||||
# If provided, the generated target is excluded from the 'all' target.
|
||||
#
|
||||
function(add_header_test target)
|
||||
cmake_parse_arguments(ARGS "EXCLUDE_FROM_ALL" # options
|
||||
"" # 1 value args
|
||||
"HEADERS;EXCLUDE" # multivalued args
|
||||
${ARGN})
|
||||
if (NOT ARGS_HEADERS)
|
||||
message(FATAL_ERROR "The `HEADERS` argument must be provided.")
|
||||
endif()
|
||||
|
||||
if (ARGS_EXCLUDE_FROM_ALL)
|
||||
set(ARGS_EXCLUDE_FROM_ALL "EXCLUDE_FROM_ALL")
|
||||
else()
|
||||
set(ARGS_EXCLUDE_FROM_ALL "")
|
||||
endif()
|
||||
|
||||
foreach(header ${ARGS_HEADERS})
|
||||
set(skip FALSE)
|
||||
foreach(exclude ${ARGS_EXCLUDE})
|
||||
if (${header} MATCHES ${exclude})
|
||||
set(skip TRUE)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
if (skip)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
get_filename_component(filename "${header}" NAME_WE)
|
||||
get_filename_component(directory "${header}" DIRECTORY)
|
||||
|
||||
set(source "${CMAKE_CURRENT_BINARY_DIR}/headers/${directory}/${filename}.cpp")
|
||||
if (NOT EXISTS "${source}")
|
||||
file(WRITE "${source}" "#include <${header}>")
|
||||
endif()
|
||||
list(APPEND sources "${source}")
|
||||
endforeach()
|
||||
|
||||
set(standalone_main "${CMAKE_CURRENT_BINARY_DIR}/headers/_standalone_main.cpp")
|
||||
if (NOT EXISTS "${standalone_main}")
|
||||
file(WRITE "${standalone_main}" "int main() { }")
|
||||
endif()
|
||||
add_executable(${target}
|
||||
${ARGS_EXCLUDE_FROM_ALL}
|
||||
${sources}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/headers/_standalone_main.cpp"
|
||||
)
|
||||
endfunction()
|
Reference in New Issue
Block a user