diff --git a/CMakeLists.txt b/CMakeLists.txt index f03cef2f..9e8b10bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -cmake_minimum_required(VERSION 3.19) +cmake_minimum_required(VERSION 3.24) project(mp-units-dev LANGUAGES CXX) list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") @@ -74,7 +74,3 @@ add_subdirectory(example) enable_testing() add_subdirectory(test) - -# tests for standalone headers -include(TestPublicHeaders) -add_public_header_test(test_headers mp-units::mp-units) diff --git a/cmake/TestHeaders.cmake b/cmake/TestHeaders.cmake deleted file mode 100644 index 154e4fd5..00000000 --- a/cmake/TestHeaders.cmake +++ /dev/null @@ -1,130 +0,0 @@ -# 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 -# -# 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( [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 -# ---------- -# : -# 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}>") - file(APPEND "${source}" "\n#include <${header}>") # do it twice to ensure that header guards are provided - 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() diff --git a/cmake/TestPublicHeaders.cmake b/cmake/TestPublicHeaders.cmake deleted file mode 100644 index 38a50770..00000000 --- a/cmake/TestPublicHeaders.cmake +++ /dev/null @@ -1,67 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2018 Mateusz Pusz -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -cmake_minimum_required(VERSION 3.19) - -function(get_target_linked_targets target targets_out) - get_target_property(linked_libs ${target} INTERFACE_LINK_LIBRARIES) - foreach(linked_lib ${linked_libs}) - if(NOT linked_lib IN_LIST targets) - if(TARGET ${linked_lib}) - get_target_linked_targets(${linked_lib} child_targets) - list(APPEND targets ${linked_lib} ${child_targets}) - endif() - endif() - endforeach() - - list(REMOVE_DUPLICATES targets) - list(REMOVE_DUPLICATES link_libs) - set(${targets_out} ${targets} PARENT_SCOPE) -endfunction() - -function(get_target_sources target paths_out) - get_target_linked_targets(${target} targets) - list(APPEND targets ${target}) - - foreach(t ${targets}) - get_target_property(sources ${t} HEADER_SET) - if(sources) - get_target_property(source_dir ${t} SOURCE_DIR) - foreach(f ${sources}) - file(REAL_PATH "${f}" path BASE_DIRECTORY "${source_dir}") - file(RELATIVE_PATH path ${CMAKE_CURRENT_LIST_DIR} "${path}") - list(APPEND paths "${path}") - endforeach() - endif() - endforeach() - - set(${paths_out} ${paths} PARENT_SCOPE) -endfunction() - -include(TestHeaders) - -function(add_public_header_test target test_target) - get_target_sources(${test_target} sources) - add_header_test(${target} HEADERS ${sources}) - target_link_libraries(${target} PRIVATE ${test_target}) - target_include_directories(${target} PRIVATE .) -endfunction() diff --git a/conanfile.py b/conanfile.py index 52d68686..40df3a0e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -60,7 +60,7 @@ class MPUnitsConan(ConanFile): default_options = { "cxx_modules": False, } - + tool_requires = "cmake/[>=3.28.1]" exports = ["LICENSE.md"] exports_sources = [ "docs/*", @@ -140,6 +140,8 @@ class MPUnitsConan(ConanFile): def generate(self): tc = CMakeToolchain(self) + if self._build_all: + tc.variables["CMAKE_VERIFY_INTERFACE_HEADER_SETS"] = True if self.options.cxx_modules: tc.variables["CMAKE_CXX_SCAN_FOR_MODULES"] = True tc.variables["MP_UNITS_BUILD_CXX_MODULES"] = True @@ -154,6 +156,7 @@ class MPUnitsConan(ConanFile): cmake.configure(build_script_folder=None if self._build_all else "src") cmake.build() if self._build_all: + cmake.build(target="all_verify_interface_header_sets") cmake.test() def package_id(self): diff --git a/docs/getting_started/installation_and_usage.md b/docs/getting_started/installation_and_usage.md index 92b4d41c..9e4163c7 100644 --- a/docs/getting_started/installation_and_usage.md +++ b/docs/getting_started/installation_and_usage.md @@ -460,6 +460,7 @@ the `conan build` with `conan install` command and then follow with a regular CM ```shell cmake --preset conan-default cmake --build --preset conan-release +cmake --build --preset conan-release --target all_verify_interface_header_sets cmake --build --preset conan-release --target test ```