From cbfc102169fbab7aeb6db8be9fc49824c9428637 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Fri, 2 Feb 2024 16:04:54 +0900 Subject: [PATCH] use cmake 3.20 version for gtest modules --- Sofa/framework/Testing/CMakeLists.txt | 5 + Sofa/framework/Testing/cmake/FindGTest.cmake | 294 +++++++++ .../cmake/FindPackageHandleStandardArgs.cmake | 605 ++++++++++++++++++ .../Testing/cmake/FindPackageMessage.cmake | 48 ++ Sofa/framework/Testing/cmake/GoogleTest.cmake | 559 ++++++++++++++++ .../Testing/cmake/GoogleTestAddTests.cmake | 188 ++++++ 6 files changed, 1699 insertions(+) create mode 100644 Sofa/framework/Testing/cmake/FindGTest.cmake create mode 100644 Sofa/framework/Testing/cmake/FindPackageHandleStandardArgs.cmake create mode 100644 Sofa/framework/Testing/cmake/FindPackageMessage.cmake create mode 100644 Sofa/framework/Testing/cmake/GoogleTest.cmake create mode 100644 Sofa/framework/Testing/cmake/GoogleTestAddTests.cmake diff --git a/Sofa/framework/Testing/CMakeLists.txt b/Sofa/framework/Testing/CMakeLists.txt index 635b7f6c29d1..661c9f7dda1a 100644 --- a/Sofa/framework/Testing/CMakeLists.txt +++ b/Sofa/framework/Testing/CMakeLists.txt @@ -1,6 +1,11 @@ cmake_minimum_required(VERSION 3.12) project(Sofa.Testing LANGUAGES CXX) +# Add cmake modules file to support fetched gtest configuration (fixed in cmake >= 3.20) +if (${CMAKE_VERSION} VERSION_LESS 3.20) + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +endif() + # Enable testing features of cmake, like the add_test() command. enable_testing() diff --git a/Sofa/framework/Testing/cmake/FindGTest.cmake b/Sofa/framework/Testing/cmake/FindGTest.cmake new file mode 100644 index 000000000000..8e22f79198d9 --- /dev/null +++ b/Sofa/framework/Testing/cmake/FindGTest.cmake @@ -0,0 +1,294 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindGTest +--------- + +Locate the Google C++ Testing Framework. + +.. versionadded:: 3.20 + Upstream ``GTestConfig.cmake`` is used if possible. + +Imported targets +^^^^^^^^^^^^^^^^ + +.. versionadded:: 3.20 + This module defines the following :prop_tgt:`IMPORTED` targets: + +``GTest::gtest`` + The Google Test ``gtest`` library, if found; adds Thread::Thread + automatically +``GTest::gtest_main`` + The Google Test ``gtest_main`` library, if found + +.. deprecated:: 3.20 + For backwards compatibility, this module defines additionally the + following deprecated :prop_tgt:`IMPORTED` targets (available since 3.5): + +``GTest::GTest`` + The Google Test ``gtest`` library, if found; adds Thread::Thread + automatically +``GTest::Main`` + The Google Test ``gtest_main`` library, if found + + +Result variables +^^^^^^^^^^^^^^^^ + +This module will set the following variables in your project: + +``GTest_FOUND`` + Found the Google Testing framework +``GTEST_INCLUDE_DIRS`` + the directory containing the Google Test headers + +The library variables below are set as normal variables. These +contain debug/optimized keywords when a debugging library is found. + +``GTEST_LIBRARIES`` + The Google Test ``gtest`` library; note it also requires linking + with an appropriate thread library +``GTEST_MAIN_LIBRARIES`` + The Google Test ``gtest_main`` library +``GTEST_BOTH_LIBRARIES`` + Both ``gtest`` and ``gtest_main`` + +Cache variables +^^^^^^^^^^^^^^^ + +The following cache variables may also be set: + +``GTEST_ROOT`` + The root directory of the Google Test installation (may also be + set as an environment variable) +``GTEST_MSVC_SEARCH`` + If compiling with MSVC, this variable can be set to ``MT`` or + ``MD`` (the default) to enable searching a GTest build tree + + +Example usage +^^^^^^^^^^^^^ + +:: + + enable_testing() + find_package(GTest REQUIRED) + + add_executable(foo foo.cc) + target_link_libraries(foo GTest::gtest GTest::gtest_main) + + add_test(AllTestsInFoo foo) + + +Deeper integration with CTest +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +See :module:`GoogleTest` for information on the :command:`gtest_add_tests` +and :command:`gtest_discover_tests` commands. + +.. versionchanged:: 3.9 + Previous CMake versions defined :command:`gtest_add_tests` macro in this + module. +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/GoogleTest.cmake) + +function(__gtest_append_debugs _endvar _library) + if(${_library} AND ${_library}_DEBUG) + set(_output optimized ${${_library}} debug ${${_library}_DEBUG}) + else() + set(_output ${${_library}}) + endif() + set(${_endvar} ${_output} PARENT_SCOPE) +endfunction() + +function(__gtest_find_library _name) + find_library(${_name} + NAMES ${ARGN} + HINTS + ENV GTEST_ROOT + ${GTEST_ROOT} + PATH_SUFFIXES ${_gtest_libpath_suffixes} + ) + mark_as_advanced(${_name}) +endfunction() + +macro(__gtest_determine_windows_library_type _var) + if(EXISTS "${${_var}}") + file(TO_NATIVE_PATH "${${_var}}" _lib_path) + get_filename_component(_name "${${_var}}" NAME_WE) + file(STRINGS "${${_var}}" _match REGEX "${_name}\\.dll" LIMIT_COUNT 1) + if(NOT _match STREQUAL "") + set(${_var}_TYPE SHARED PARENT_SCOPE) + else() + set(${_var}_TYPE UNKNOWN PARENT_SCOPE) + endif() + return() + endif() +endmacro() + +function(__gtest_determine_library_type _var) + if(WIN32) + # For now, at least, only Windows really needs to know the library type + __gtest_determine_windows_library_type(${_var}) + __gtest_determine_windows_library_type(${_var}_RELEASE) + __gtest_determine_windows_library_type(${_var}_DEBUG) + endif() + # If we get here, no determination was made from the above checks + set(${_var}_TYPE UNKNOWN PARENT_SCOPE) +endfunction() + +function(__gtest_import_library _target _var _config) + if(_config) + set(_config_suffix "_${_config}") + else() + set(_config_suffix "") + endif() + + set(_lib "${${_var}${_config_suffix}}") + if(EXISTS "${_lib}") + if(_config) + set_property(TARGET ${_target} APPEND PROPERTY + IMPORTED_CONFIGURATIONS ${_config}) + endif() + set_target_properties(${_target} PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES${_config_suffix} "CXX") + if(WIN32 AND ${_var}_TYPE STREQUAL SHARED) + set_target_properties(${_target} PROPERTIES + IMPORTED_IMPLIB${_config_suffix} "${_lib}") + else() + set_target_properties(${_target} PROPERTIES + IMPORTED_LOCATION${_config_suffix} "${_lib}") + endif() + endif() +endfunction() + +function(__gtest_define_backwards_compatible_library_targets) + set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES} PARENT_SCOPE) + + # Add targets mapping the same library names as defined in + # older versions of CMake's FindGTest + if(NOT TARGET GTest::GTest) + add_library(GTest::GTest INTERFACE IMPORTED) + target_link_libraries(GTest::GTest INTERFACE GTest::gtest) + endif() + if(NOT TARGET GTest::Main) + add_library(GTest::Main INTERFACE IMPORTED) + target_link_libraries(GTest::Main INTERFACE GTest::gtest_main) + endif() +endfunction() + +# + +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) + +# first specifically look for the CMake version of GTest +find_package(GTest QUIET NO_MODULE) + +# if we found the GTest cmake package then we are done, and +# can print what we found and return. +if(GTest_FOUND) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(GTest HANDLE_COMPONENTS CONFIG_MODE) + + set(GTEST_LIBRARIES GTest::gtest) + set(GTEST_MAIN_LIBRARIES GTest::gtest_main) + + __gtest_define_backwards_compatible_library_targets() + + return() +endif() + +if(NOT DEFINED GTEST_MSVC_SEARCH) + set(GTEST_MSVC_SEARCH MD) +endif() + +set(_gtest_libpath_suffixes lib) +if(MSVC) + if(GTEST_MSVC_SEARCH STREQUAL "MD") + list(APPEND _gtest_libpath_suffixes + msvc/gtest-md/Debug + msvc/gtest-md/Release + msvc/x64/Debug + msvc/x64/Release + msvc/2010/gtest-md/Win32-Debug + msvc/2010/gtest-md/Win32-Release + msvc/2010/gtest-md/x64-Debug + msvc/2010/gtest-md/x64-Release + ) + elseif(GTEST_MSVC_SEARCH STREQUAL "MT") + list(APPEND _gtest_libpath_suffixes + msvc/gtest/Debug + msvc/gtest/Release + msvc/x64/Debug + msvc/x64/Release + msvc/2010/gtest/Win32-Debug + msvc/2010/gtest/Win32-Release + msvc/2010/gtest/x64-Debug + msvc/2010/gtest/x64-Release + ) + endif() +endif() + + +find_path(GTEST_INCLUDE_DIR gtest/gtest.h + HINTS + $ENV{GTEST_ROOT}/include + ${GTEST_ROOT}/include +) +mark_as_advanced(GTEST_INCLUDE_DIR) + +if(MSVC AND GTEST_MSVC_SEARCH STREQUAL "MD") + # The provided /MD project files for Google Test add -md suffixes to the + # library names. + __gtest_find_library(GTEST_LIBRARY gtest-md gtest) + __gtest_find_library(GTEST_LIBRARY_DEBUG gtest-mdd gtestd) + __gtest_find_library(GTEST_MAIN_LIBRARY gtest_main-md gtest_main) + __gtest_find_library(GTEST_MAIN_LIBRARY_DEBUG gtest_main-mdd gtest_maind) +else() + __gtest_find_library(GTEST_LIBRARY gtest) + __gtest_find_library(GTEST_LIBRARY_DEBUG gtestd) + __gtest_find_library(GTEST_MAIN_LIBRARY gtest_main) + __gtest_find_library(GTEST_MAIN_LIBRARY_DEBUG gtest_maind) +endif() + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GTest DEFAULT_MSG GTEST_LIBRARY GTEST_INCLUDE_DIR GTEST_MAIN_LIBRARY) + +if(GTest_FOUND) + set(GTEST_INCLUDE_DIRS ${GTEST_INCLUDE_DIR}) + __gtest_append_debugs(GTEST_LIBRARIES GTEST_LIBRARY) + __gtest_append_debugs(GTEST_MAIN_LIBRARIES GTEST_MAIN_LIBRARY) + + find_package(Threads QUIET) + + if(NOT TARGET GTest::gtest) + __gtest_determine_library_type(GTEST_LIBRARY) + add_library(GTest::gtest ${GTEST_LIBRARY_TYPE} IMPORTED) + if(TARGET Threads::Threads) + set_target_properties(GTest::gtest PROPERTIES + INTERFACE_LINK_LIBRARIES Threads::Threads) + endif() + if(GTEST_LIBRARY_TYPE STREQUAL "SHARED") + set_target_properties(GTest::gtest PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1") + endif() + if(GTEST_INCLUDE_DIRS) + set_target_properties(GTest::gtest PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIRS}") + endif() + __gtest_import_library(GTest::gtest GTEST_LIBRARY "") + __gtest_import_library(GTest::gtest GTEST_LIBRARY "RELEASE") + __gtest_import_library(GTest::gtest GTEST_LIBRARY "DEBUG") + endif() + if(NOT TARGET GTest::gtest_main) + __gtest_determine_library_type(GTEST_MAIN_LIBRARY) + add_library(GTest::gtest_main ${GTEST_MAIN_LIBRARY_TYPE} IMPORTED) + set_target_properties(GTest::gtest_main PROPERTIES + INTERFACE_LINK_LIBRARIES "GTest::gtest") + __gtest_import_library(GTest::gtest_main GTEST_MAIN_LIBRARY "") + __gtest_import_library(GTest::gtest_main GTEST_MAIN_LIBRARY "RELEASE") + __gtest_import_library(GTest::gtest_main GTEST_MAIN_LIBRARY "DEBUG") + endif() + + __gtest_define_backwards_compatible_library_targets() +endif() diff --git a/Sofa/framework/Testing/cmake/FindPackageHandleStandardArgs.cmake b/Sofa/framework/Testing/cmake/FindPackageHandleStandardArgs.cmake new file mode 100644 index 000000000000..fbcf7cd88bc9 --- /dev/null +++ b/Sofa/framework/Testing/cmake/FindPackageHandleStandardArgs.cmake @@ -0,0 +1,605 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindPackageHandleStandardArgs +----------------------------- + +This module provides functions intended to be used in :ref:`Find Modules` +implementing :command:`find_package()` calls. + +.. command:: find_package_handle_standard_args + + This command handles the ``REQUIRED``, ``QUIET`` and version-related + arguments of :command:`find_package`. It also sets the + ``_FOUND`` variable. The package is considered found if all + variables listed contain valid results, e.g. valid filepaths. + + There are two signatures: + + .. code-block:: cmake + + find_package_handle_standard_args( + (DEFAULT_MSG|) + ... + ) + + find_package_handle_standard_args( + [FOUND_VAR ] + [REQUIRED_VARS ...] + [VERSION_VAR ] + [HANDLE_VERSION_RANGE] + [HANDLE_COMPONENTS] + [CONFIG_MODE] + [NAME_MISMATCHED] + [REASON_FAILURE_MESSAGE ] + [FAIL_MESSAGE ] + ) + + The ``_FOUND`` variable will be set to ``TRUE`` if all + the variables ``...`` are valid and any optional + constraints are satisfied, and ``FALSE`` otherwise. A success or + failure message may be displayed based on the results and on + whether the ``REQUIRED`` and/or ``QUIET`` option was given to + the :command:`find_package` call. + + The options are: + + ``(DEFAULT_MSG|)`` + In the simple signature this specifies the failure message. + Use ``DEFAULT_MSG`` to ask for a default message to be computed + (recommended). Not valid in the full signature. + + ``FOUND_VAR `` + .. deprecated:: 3.3 + + Specifies either ``_FOUND`` or + ``_FOUND`` as the result variable. This exists only + for compatibility with older versions of CMake and is now ignored. + Result variables of both names are always set for compatibility. + + ``REQUIRED_VARS ...`` + Specify the variables which are required for this package. + These may be named in the generated failure message asking the + user to set the missing variable values. Therefore these should + typically be cache entries such as ``FOO_LIBRARY`` and not output + variables like ``FOO_LIBRARIES``. + + .. versionchanged:: 3.18 + If ``HANDLE_COMPONENTS`` is specified, this option can be omitted. + + ``VERSION_VAR `` + Specify the name of a variable that holds the version of the package + that has been found. This version will be checked against the + (potentially) specified required version given to the + :command:`find_package` call, including its ``EXACT`` option. + The default messages include information about the required + version and the version which has been actually found, both + if the version is ok or not. + + ``HANDLE_VERSION_RANGE`` + .. versionadded:: 3.19 + + Enable handling of a version range, if one is specified. Without this + option, a developer warning will be displayed if a version range is + specified. + + ``HANDLE_COMPONENTS`` + Enable handling of package components. In this case, the command + will report which components have been found and which are missing, + and the ``_FOUND`` variable will be set to ``FALSE`` + if any of the required components (i.e. not the ones listed after + the ``OPTIONAL_COMPONENTS`` option of :command:`find_package`) are + missing. + + ``CONFIG_MODE`` + Specify that the calling find module is a wrapper around a + call to ``find_package( NO_MODULE)``. This implies + a ``VERSION_VAR`` value of ``_VERSION``. The command + will automatically check whether the package configuration file + was found. + + ``REASON_FAILURE_MESSAGE `` + .. versionadded:: 3.16 + + Specify a custom message of the reason for the failure which will be + appended to the default generated message. + + ``FAIL_MESSAGE `` + Specify a custom failure message instead of using the default + generated message. Not recommended. + + ``NAME_MISMATCHED`` + .. versionadded:: 3.17 + + Indicate that the ```` does not match + ``${CMAKE_FIND_PACKAGE_NAME}``. This is usually a mistake and raises a + warning, but it may be intentional for usage of the command for components + of a larger package. + +Example for the simple signature: + +.. code-block:: cmake + + find_package_handle_standard_args(LibXml2 DEFAULT_MSG + LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) + +The ``LibXml2`` package is considered to be found if both +``LIBXML2_LIBRARY`` and ``LIBXML2_INCLUDE_DIR`` are valid. +Then also ``LibXml2_FOUND`` is set to ``TRUE``. If it is not found +and ``REQUIRED`` was used, it fails with a +:command:`message(FATAL_ERROR)`, independent whether ``QUIET`` was +used or not. If it is found, success will be reported, including +the content of the first ````. On repeated CMake runs, +the same message will not be printed again. + +.. note:: + + If ```` does not match ``CMAKE_FIND_PACKAGE_NAME`` for the + calling module, a warning that there is a mismatch is given. The + ``FPHSA_NAME_MISMATCHED`` variable may be set to bypass the warning if using + the old signature and the ``NAME_MISMATCHED`` argument using the new + signature. To avoid forcing the caller to require newer versions of CMake for + usage, the variable's value will be used if defined when the + ``NAME_MISMATCHED`` argument is not passed for the new signature (but using + both is an error).. + +Example for the full signature: + +.. code-block:: cmake + + find_package_handle_standard_args(LibArchive + REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR + VERSION_VAR LibArchive_VERSION) + +In this case, the ``LibArchive`` package is considered to be found if +both ``LibArchive_LIBRARY`` and ``LibArchive_INCLUDE_DIR`` are valid. +Also the version of ``LibArchive`` will be checked by using the version +contained in ``LibArchive_VERSION``. Since no ``FAIL_MESSAGE`` is given, +the default messages will be printed. + +Another example for the full signature: + +.. code-block:: cmake + + find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) + find_package_handle_standard_args(Automoc4 CONFIG_MODE) + +In this case, a ``FindAutmoc4.cmake`` module wraps a call to +``find_package(Automoc4 NO_MODULE)`` and adds an additional search +directory for ``automoc4``. Then the call to +``find_package_handle_standard_args`` produces a proper success/failure +message. + +.. command:: find_package_check_version + + .. versionadded:: 3.19 + + Helper function which can be used to check if a ```` is valid + against version-related arguments of :command:`find_package`. + + .. code-block:: cmake + + find_package_check_version( + [HANDLE_VERSION_RANGE] + [RESULT_MESSAGE_VARIABLE ] + ) + + The ```` will hold a boolean value giving the result of the check. + + The options are: + + ``HANDLE_VERSION_RANGE`` + Enable handling of a version range, if one is specified. Without this + option, a developer warning will be displayed if a version range is + specified. + + ``RESULT_MESSAGE_VARIABLE `` + Specify a variable to get back a message describing the result of the check. + +Example for the usage: + +.. code-block:: cmake + + find_package_check_version(1.2.3 result HANDLE_VERSION_RANGE + RESULT_MESSAGE_VARIABLE reason) + if (result) + message (STATUS "${reason}") + else() + message (FATAL_ERROR "${reason}") + endif() +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake) + + +cmake_policy(PUSH) +# numbers and boolean constants +cmake_policy (SET CMP0012 NEW) +# IN_LIST operator +cmake_policy (SET CMP0057 NEW) + + +# internal helper macro +macro(_FPHSA_FAILURE_MESSAGE _msg) + set (__msg "${_msg}") + if (FPHSA_REASON_FAILURE_MESSAGE) + string(APPEND __msg "\n Reason given by package: ${FPHSA_REASON_FAILURE_MESSAGE}\n") + endif() + if (${_NAME}_FIND_REQUIRED) + message(FATAL_ERROR "${__msg}") + else () + if (NOT ${_NAME}_FIND_QUIETLY) + message(STATUS "${__msg}") + endif () + endif () +endmacro() + + +# internal helper macro to generate the failure message when used in CONFIG_MODE: +macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) + # _CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: + if(${_NAME}_CONFIG) + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing:${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") + else() + # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. + # List them all in the error message: + if(${_NAME}_CONSIDERED_CONFIGS) + set(configsText "") + list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) + math(EXPR configsCount "${configsCount} - 1") + foreach(currentConfigIndex RANGE ${configsCount}) + list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) + list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) + string(APPEND configsText "\n ${filename} (version ${version})") + endforeach() + if (${_NAME}_NOT_FOUND_MESSAGE) + if (FPHSA_REASON_FAILURE_MESSAGE) + string(PREPEND FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}\n ") + else() + set(FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}") + endif() + else() + string(APPEND configsText "\n") + endif() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:${configsText}") + + else() + # Simple case: No Config-file was found at all: + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") + endif() + endif() +endmacro() + + +function(FIND_PACKAGE_CHECK_VERSION version result) + cmake_parse_arguments (PARSE_ARGV 2 FPCV "HANDLE_VERSION_RANGE;NO_AUTHOR_WARNING_VERSION_RANGE" "RESULT_MESSAGE_VARIABLE" "") + + if (FPCV_UNPARSED_ARGUMENTS) + message (FATAL_ERROR "find_package_check_version(): ${FPCV_UNPARSED_ARGUMENTS}: unexpected arguments") + endif() + if ("RESULT_MESSAGE_VARIABLE" IN_LIST FPCV_KEYWORDS_MISSING_VALUES) + message (FATAL_ERROR "find_package_check_version(): RESULT_MESSAGE_VARIABLE expects an argument") + endif() + + set (${result} FALSE PARENT_SCOPE) + if (FPCV_RESULT_MESSAGE_VARIABLE) + unset (${FPCV_RESULT_MESSAGE_VARIABLE} PARENT_SCOPE) + endif() + + if (_CMAKE_FPHSA_PACKAGE_NAME) + set (package "${_CMAKE_FPHSA_PACKAGE_NAME}") + elseif (CMAKE_FIND_PACKAGE_NAME) + set (package "${CMAKE_FIND_PACKAGE_NAME}") + else() + message (FATAL_ERROR "find_package_check_version(): Cannot be used outside a 'Find Module'") + endif() + + if (NOT FPCV_NO_AUTHOR_WARNING_VERSION_RANGE + AND ${package}_FIND_VERSION_RANGE AND NOT FPCV_HANDLE_VERSION_RANGE) + message(AUTHOR_WARNING + "`find_package()` specify a version range but the option " + "HANDLE_VERSION_RANGE` is not passed to `find_package_check_version()`. " + "Only the lower endpoint of the range will be used.") + endif() + + + set (version_ok FALSE) + unset (version_msg) + + if (FPCV_HANDLE_VERSION_RANGE AND ${package}_FIND_VERSION_RANGE) + if ((${package}_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE" + AND version VERSION_GREATER_EQUAL ${package}_FIND_VERSION_MIN) + AND ((${package}_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" + AND version VERSION_LESS_EQUAL ${package}_FIND_VERSION_MAX) + OR (${package}_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" + AND version VERSION_LESS ${package}_FIND_VERSION_MAX))) + set (version_ok TRUE) + set(version_msg "(found suitable version \"${version}\", required range is \"${${package}_FIND_VERSION_RANGE}\")") + else() + set(version_msg "Found unsuitable version \"${version}\", required range is \"${${package}_FIND_VERSION_RANGE}\"") + endif() + elseif (DEFINED ${package}_FIND_VERSION) + if(${package}_FIND_VERSION_EXACT) # exact version required + # count the dots in the version string + string(REGEX REPLACE "[^.]" "" version_dots "${version}") + # add one dot because there is one dot more than there are components + string(LENGTH "${version_dots}." version_dots) + if (version_dots GREATER ${package}_FIND_VERSION_COUNT) + # Because of the C++ implementation of find_package() ${package}_FIND_VERSION_COUNT + # is at most 4 here. Therefore a simple lookup table is used. + if (${package}_FIND_VERSION_COUNT EQUAL 1) + set(version_regex "[^.]*") + elseif (${package}_FIND_VERSION_COUNT EQUAL 2) + set(version_regex "[^.]*\\.[^.]*") + elseif (${package}_FIND_VERSION_COUNT EQUAL 3) + set(version_regex "[^.]*\\.[^.]*\\.[^.]*") + else() + set(version_regex "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*") + endif() + string(REGEX REPLACE "^(${version_regex})\\..*" "\\1" version_head "${version}") + if (NOT ${package}_FIND_VERSION VERSION_EQUAL version_head) + set(version_msg "Found unsuitable version \"${version}\", but required is exact version \"${${package}_FIND_VERSION}\"") + else () + set(version_ok TRUE) + set(version_msg "(found suitable exact version \"${_FOUND_VERSION}\")") + endif () + else () + if (NOT ${package}_FIND_VERSION VERSION_EQUAL version) + set(version_msg "Found unsuitable version \"${version}\", but required is exact version \"${${package}_FIND_VERSION}\"") + else () + set(version_ok TRUE) + set(version_msg "(found suitable exact version \"${version}\")") + endif () + endif () + else() # minimum version + if (${package}_FIND_VERSION VERSION_GREATER version) + set(version_msg "Found unsuitable version \"${version}\", but required is at least \"${${package}_FIND_VERSION}\"") + else() + set(version_ok TRUE) + set(version_msg "(found suitable version \"${version}\", minimum required is \"${${package}_FIND_VERSION}\")") + endif() + endif() + else () + set(version_ok TRUE) + set(version_msg "(found version \"${version}\")") + endif() + + set (${result} ${version_ok} PARENT_SCOPE) + if (FPCV_RESULT_MESSAGE_VARIABLE) + set (${FPCV_RESULT_MESSAGE_VARIABLE} "${version_msg}" PARENT_SCOPE) + endif() +endfunction() + + +function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) + + # Set up the arguments for `cmake_parse_arguments`. + set(options CONFIG_MODE HANDLE_COMPONENTS NAME_MISMATCHED HANDLE_VERSION_RANGE) + set(oneValueArgs FAIL_MESSAGE REASON_FAILURE_MESSAGE VERSION_VAR FOUND_VAR) + set(multiValueArgs REQUIRED_VARS) + + # Check whether we are in 'simple' or 'extended' mode: + set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) + list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) + + unset(FPHSA_NAME_MISMATCHED_override) + if (DEFINED FPHSA_NAME_MISMATCHED) + # If the variable NAME_MISMATCHED variable is set, error if it is passed as + # an argument. The former is for old signatures, the latter is for new + # signatures. + list(FIND ARGN "NAME_MISMATCHED" name_mismatched_idx) + if (NOT name_mismatched_idx EQUAL "-1") + message(FATAL_ERROR + "The `NAME_MISMATCHED` argument may only be specified by the argument or " + "the variable, not both.") + endif () + + # But use the variable if it is not an argument to avoid forcing minimum + # CMake version bumps for calling modules. + set(FPHSA_NAME_MISMATCHED_override "${FPHSA_NAME_MISMATCHED}") + endif () + + if(${INDEX} EQUAL -1) + set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) + set(FPHSA_REQUIRED_VARS ${ARGN}) + set(FPHSA_VERSION_VAR) + else() + cmake_parse_arguments(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) + + if(FPHSA_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") + endif() + + if(NOT FPHSA_FAIL_MESSAGE) + set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") + endif() + + # In config-mode, we rely on the variable _CONFIG, which is set by find_package() + # when it successfully found the config-file, including version checking: + if(FPHSA_CONFIG_MODE) + list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) + list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) + set(FPHSA_VERSION_VAR ${_NAME}_VERSION) + endif() + + if(NOT FPHSA_REQUIRED_VARS AND NOT FPHSA_HANDLE_COMPONENTS) + message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") + endif() + endif() + + if (DEFINED FPHSA_NAME_MISMATCHED_override) + set(FPHSA_NAME_MISMATCHED "${FPHSA_NAME_MISMATCHED_override}") + endif () + + if (DEFINED CMAKE_FIND_PACKAGE_NAME + AND NOT FPHSA_NAME_MISMATCHED + AND NOT _NAME STREQUAL CMAKE_FIND_PACKAGE_NAME) + message(AUTHOR_WARNING + "The package name passed to `find_package_handle_standard_args` " + "(${_NAME}) does not match the name of the calling package " + "(${CMAKE_FIND_PACKAGE_NAME}). This can lead to problems in calling " + "code that expects `find_package` result variables (e.g., `_FOUND`) " + "to follow a certain pattern.") + endif () + + if (${_NAME}_FIND_VERSION_RANGE AND NOT FPHSA_HANDLE_VERSION_RANGE) + message(AUTHOR_WARNING + "`find_package()` specify a version range but the module ${_NAME} does " + "not support this capability. Only the lower endpoint of the range " + "will be used.") + endif() + + # to propagate package name to FIND_PACKAGE_CHECK_VERSION + set(_CMAKE_FPHSA_PACKAGE_NAME "${_NAME}") + + # now that we collected all arguments, process them + + if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG") + set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") + endif() + + if (FPHSA_REQUIRED_VARS) + list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) + endif() + + string(TOUPPER ${_NAME} _NAME_UPPER) + string(TOLOWER ${_NAME} _NAME_LOWER) + + if(FPHSA_FOUND_VAR) + set(_FOUND_VAR_UPPER ${_NAME_UPPER}_FOUND) + set(_FOUND_VAR_MIXED ${_NAME}_FOUND) + if(FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_MIXED OR FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_UPPER) + set(_FOUND_VAR ${FPHSA_FOUND_VAR}) + else() + message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_FOUND_VAR_MIXED}\" and \"${_FOUND_VAR_UPPER}\" are valid names.") + endif() + else() + set(_FOUND_VAR ${_NAME_UPPER}_FOUND) + endif() + + # collect all variables which were not found, so they can be printed, so the + # user knows better what went wrong (#6375) + set(MISSING_VARS "") + set(DETAILS "") + # check if all passed variables are valid + set(FPHSA_FOUND_${_NAME} TRUE) + foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) + if(NOT ${_CURRENT_VAR}) + set(FPHSA_FOUND_${_NAME} FALSE) + string(APPEND MISSING_VARS " ${_CURRENT_VAR}") + else() + string(APPEND DETAILS "[${${_CURRENT_VAR}}]") + endif() + endforeach() + if(FPHSA_FOUND_${_NAME}) + set(${_NAME}_FOUND TRUE) + set(${_NAME_UPPER}_FOUND TRUE) + else() + set(${_NAME}_FOUND FALSE) + set(${_NAME_UPPER}_FOUND FALSE) + endif() + + # component handling + unset(FOUND_COMPONENTS_MSG) + unset(MISSING_COMPONENTS_MSG) + + if(FPHSA_HANDLE_COMPONENTS) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(${_NAME}_${comp}_FOUND) + + if(NOT DEFINED FOUND_COMPONENTS_MSG) + set(FOUND_COMPONENTS_MSG "found components:") + endif() + string(APPEND FOUND_COMPONENTS_MSG " ${comp}") + + else() + + if(NOT DEFINED MISSING_COMPONENTS_MSG) + set(MISSING_COMPONENTS_MSG "missing components:") + endif() + string(APPEND MISSING_COMPONENTS_MSG " ${comp}") + + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + string(APPEND MISSING_VARS " ${comp}") + endif() + + endif() + endforeach() + set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}") + string(APPEND DETAILS "[c${COMPONENT_MSG}]") + endif() + + # version handling: + set(VERSION_MSG "") + set(VERSION_OK TRUE) + + # check with DEFINED here as the requested or found version may be "0" + if (DEFINED ${_NAME}_FIND_VERSION) + if(DEFINED ${FPHSA_VERSION_VAR}) + set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}}) + if (FPHSA_HANDLE_VERSION_RANGE) + set (FPCV_HANDLE_VERSION_RANGE HANDLE_VERSION_RANGE) + else() + set(FPCV_HANDLE_VERSION_RANGE NO_AUTHOR_WARNING_VERSION_RANGE) + endif() + find_package_check_version ("${_FOUND_VERSION}" VERSION_OK RESULT_MESSAGE_VARIABLE VERSION_MSG + ${FPCV_HANDLE_VERSION_RANGE}) + else() + # if the package was not found, but a version was given, add that to the output: + if(${_NAME}_FIND_VERSION_EXACT) + set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") + elseif (FPHSA_HANDLE_VERSION_RANGE AND ${_NAME}_FIND_VERSION_RANGE) + set(VERSION_MSG "(Required is version range \"${${_NAME}_FIND_VERSION_RANGE}\")") + else() + set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") + endif() + endif() + else () + # Check with DEFINED as the found version may be 0. + if(DEFINED ${FPHSA_VERSION_VAR}) + set(VERSION_MSG "(found version \"${${FPHSA_VERSION_VAR}}\")") + endif() + endif () + + if(VERSION_OK) + string(APPEND DETAILS "[v${${FPHSA_VERSION_VAR}}(${${_NAME}_FIND_VERSION})]") + else() + set(${_NAME}_FOUND FALSE) + endif() + + + # print the result: + if (${_NAME}_FOUND) + FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}") + else () + + if(FPHSA_CONFIG_MODE) + _FPHSA_HANDLE_FAILURE_CONFIG_MODE() + else() + if(NOT VERSION_OK) + set(RESULT_MSG) + if (_FIRST_REQUIRED_VAR) + string (APPEND RESULT_MSG "found ${${_FIRST_REQUIRED_VAR}}") + endif() + if (COMPONENT_MSG) + if (RESULT_MSG) + string (APPEND RESULT_MSG ", ") + endif() + string (APPEND RESULT_MSG "${FOUND_COMPONENTS_MSG}") + endif() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (${RESULT_MSG})") + else() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing:${MISSING_VARS}) ${VERSION_MSG}") + endif() + endif() + + endif () + + set(${_NAME}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) + set(${_NAME_UPPER}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) +endfunction() + + +cmake_policy(POP) diff --git a/Sofa/framework/Testing/cmake/FindPackageMessage.cmake b/Sofa/framework/Testing/cmake/FindPackageMessage.cmake new file mode 100644 index 000000000000..0628b9816911 --- /dev/null +++ b/Sofa/framework/Testing/cmake/FindPackageMessage.cmake @@ -0,0 +1,48 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindPackageMessage +------------------ + +.. code-block:: cmake + + find_package_message( "message for user" "find result details") + +This function is intended to be used in FindXXX.cmake modules files. +It will print a message once for each unique find result. This is +useful for telling the user where a package was found. The first +argument specifies the name (XXX) of the package. The second argument +specifies the message to display. The third argument lists details +about the find result so that if they change the message will be +displayed again. The macro also obeys the QUIET argument to the +find_package command. + +Example: + +.. code-block:: cmake + + if(X11_FOUND) + find_package_message(X11 "Found X11: ${X11_X11_LIB}" + "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]") + else() + ... + endif() +#]=======================================================================] + +function(find_package_message pkg msg details) + # Avoid printing a message repeatedly for the same find result. + if(NOT ${pkg}_FIND_QUIETLY) + string(REPLACE "\n" "" details "${details}") + set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg}) + if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}") + # The message has not yet been printed. + message(STATUS "${msg}") + + # Save the find details in the cache to avoid printing the same + # message again. + set("${DETAILS_VAR}" "${details}" + CACHE INTERNAL "Details about finding ${pkg}") + endif() + endif() +endfunction() diff --git a/Sofa/framework/Testing/cmake/GoogleTest.cmake b/Sofa/framework/Testing/cmake/GoogleTest.cmake new file mode 100644 index 000000000000..2ea9e742502e --- /dev/null +++ b/Sofa/framework/Testing/cmake/GoogleTest.cmake @@ -0,0 +1,559 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +GoogleTest +---------- + +.. versionadded:: 3.9 + +This module defines functions to help use the Google Test infrastructure. Two +mechanisms for adding tests are provided. :command:`gtest_add_tests` has been +around for some time, originally via ``find_package(GTest)``. +:command:`gtest_discover_tests` was introduced in CMake 3.10. + +The (older) :command:`gtest_add_tests` scans source files to identify tests. +This is usually effective, with some caveats, including in cross-compiling +environments, and makes setting additional properties on tests more convenient. +However, its handling of parameterized tests is less comprehensive, and it +requires re-running CMake to detect changes to the list of tests. + +The (newer) :command:`gtest_discover_tests` discovers tests by asking the +compiled test executable to enumerate its tests. This is more robust and +provides better handling of parameterized tests, and does not require CMake +to be re-run when tests change. However, it may not work in a cross-compiling +environment, and setting test properties is less convenient. + +More details can be found in the documentation of the respective functions. + +Both commands are intended to replace use of :command:`add_test` to register +tests, and will create a separate CTest test for each Google Test test case. +Note that this is in some cases less efficient, as common set-up and tear-down +logic cannot be shared by multiple test cases executing in the same instance. +However, it provides more fine-grained pass/fail information to CTest, which is +usually considered as more beneficial. By default, the CTest test name is the +same as the Google Test name (i.e. ``suite.testcase``); see also +``TEST_PREFIX`` and ``TEST_SUFFIX``. + +.. command:: gtest_add_tests + + Automatically add tests with CTest by scanning source code for Google Test + macros:: + + gtest_add_tests(TARGET target + [SOURCES src1...] + [EXTRA_ARGS arg1...] + [WORKING_DIRECTORY dir] + [TEST_PREFIX prefix] + [TEST_SUFFIX suffix] + [SKIP_DEPENDENCY] + [TEST_LIST outVar] + ) + + ``gtest_add_tests`` attempts to identify tests by scanning source files. + Although this is generally effective, it uses only a basic regular expression + match, which can be defeated by atypical test declarations, and is unable to + fully "split" parameterized tests. Additionally, it requires that CMake be + re-run to discover any newly added, removed or renamed tests (by default, + this means that CMake is re-run when any test source file is changed, but see + ``SKIP_DEPENDENCY``). However, it has the advantage of declaring tests at + CMake time, which somewhat simplifies setting additional properties on tests, + and always works in a cross-compiling environment. + + The options are: + + ``TARGET target`` + Specifies the Google Test executable, which must be a known CMake + executable target. CMake will substitute the location of the built + executable when running the test. + + ``SOURCES src1...`` + When provided, only the listed files will be scanned for test cases. If + this option is not given, the :prop_tgt:`SOURCES` property of the + specified ``target`` will be used to obtain the list of sources. + + ``EXTRA_ARGS arg1...`` + Any extra arguments to pass on the command line to each test case. + + ``WORKING_DIRECTORY dir`` + Specifies the directory in which to run the discovered test cases. If this + option is not provided, the current binary directory is used. + + ``TEST_PREFIX prefix`` + Specifies a ``prefix`` to be prepended to the name of each discovered test + case. This can be useful when the same source files are being used in + multiple calls to ``gtest_add_test()`` but with different ``EXTRA_ARGS``. + + ``TEST_SUFFIX suffix`` + Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of + every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may + be specified. + + ``SKIP_DEPENDENCY`` + Normally, the function creates a dependency which will cause CMake to be + re-run if any of the sources being scanned are changed. This is to ensure + that the list of discovered tests is updated. If this behavior is not + desired (as may be the case while actually writing the test cases), this + option can be used to prevent the dependency from being added. + + ``TEST_LIST outVar`` + The variable named by ``outVar`` will be populated in the calling scope + with the list of discovered test cases. This allows the caller to do + things like manipulate test properties of the discovered tests. + + Usage example: + + .. code-block:: cmake + + include(GoogleTest) + add_executable(FooTest FooUnitTest.cxx) + gtest_add_tests(TARGET FooTest + TEST_SUFFIX .noArgs + TEST_LIST noArgsTests + ) + gtest_add_tests(TARGET FooTest + EXTRA_ARGS --someArg someValue + TEST_SUFFIX .withArgs + TEST_LIST withArgsTests + ) + set_tests_properties(${noArgsTests} PROPERTIES TIMEOUT 10) + set_tests_properties(${withArgsTests} PROPERTIES TIMEOUT 20) + + For backward compatibility, the following form is also supported:: + + gtest_add_tests(exe args files...) + + ``exe`` + The path to the test executable or the name of a CMake target. + ``args`` + A ;-list of extra arguments to be passed to executable. The entire + list must be passed as a single argument. Enclose it in quotes, + or pass ``""`` for no arguments. + ``files...`` + A list of source files to search for tests and test fixtures. + Alternatively, use ``AUTO`` to specify that ``exe`` is the name + of a CMake executable target whose sources should be scanned. + + .. code-block:: cmake + + include(GoogleTest) + set(FooTestArgs --foo 1 --bar 2) + add_executable(FooTest FooUnitTest.cxx) + gtest_add_tests(FooTest "${FooTestArgs}" AUTO) + +.. command:: gtest_discover_tests + + Automatically add tests with CTest by querying the compiled test executable + for available tests:: + + gtest_discover_tests(target + [EXTRA_ARGS arg1...] + [WORKING_DIRECTORY dir] + [TEST_PREFIX prefix] + [TEST_SUFFIX suffix] + [NO_PRETTY_TYPES] [NO_PRETTY_VALUES] + [PROPERTIES name1 value1...] + [TEST_LIST var] + [DISCOVERY_TIMEOUT seconds] + [XML_OUTPUT_DIR dir] + [DISCOVERY_MODE ] + ) + + .. versionadded:: 3.10 + + ``gtest_discover_tests()`` sets up a post-build command on the test executable + that generates the list of tests by parsing the output from running the test + with the ``--gtest_list_tests`` argument. Compared to the source parsing + approach of :command:`gtest_add_tests`, this ensures that the full list of + tests, including instantiations of parameterized tests, is obtained. Since + test discovery occurs at build time, it is not necessary to re-run CMake when + the list of tests changes. + However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set + in order to function in a cross-compiling environment. + + Additionally, setting properties on tests is somewhat less convenient, since + the tests are not available at CMake time. Additional test properties may be + assigned to the set of tests as a whole using the ``PROPERTIES`` option. If + more fine-grained test control is needed, custom content may be provided + through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES` + directory property. The set of discovered tests is made accessible to such a + script via the ``_TESTS`` variable. + + The options are: + + ``target`` + Specifies the Google Test executable, which must be a known CMake + executable target. CMake will substitute the location of the built + executable when running the test. + + ``EXTRA_ARGS arg1...`` + Any extra arguments to pass on the command line to each test case. + + ``WORKING_DIRECTORY dir`` + Specifies the directory in which to run the discovered test cases. If this + option is not provided, the current binary directory is used. + + ``TEST_PREFIX prefix`` + Specifies a ``prefix`` to be prepended to the name of each discovered test + case. This can be useful when the same test executable is being used in + multiple calls to ``gtest_discover_tests()`` but with different + ``EXTRA_ARGS``. + + ``TEST_SUFFIX suffix`` + Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of + every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may + be specified. + + ``NO_PRETTY_TYPES`` + By default, the type index of type-parameterized tests is replaced by the + actual type name in the CTest test name. If this behavior is undesirable + (e.g. because the type names are unwieldy), this option will suppress this + behavior. + + ``NO_PRETTY_VALUES`` + By default, the value index of value-parameterized tests is replaced by the + actual value in the CTest test name. If this behavior is undesirable + (e.g. because the value strings are unwieldy), this option will suppress + this behavior. + + ``PROPERTIES name1 value1...`` + Specifies additional properties to be set on all tests discovered by this + invocation of ``gtest_discover_tests()``. + + ``TEST_LIST var`` + Make the list of tests available in the variable ``var``, rather than the + default ``_TESTS``. This can be useful when the same test + executable is being used in multiple calls to ``gtest_discover_tests()``. + Note that this variable is only available in CTest. + + ``DISCOVERY_TIMEOUT num`` + .. versionadded:: 3.10.3 + + Specifies how long (in seconds) CMake will wait for the test to enumerate + available tests. If the test takes longer than this, discovery (and your + build) will fail. Most test executables will enumerate their tests very + quickly, but under some exceptional circumstances, a test may require a + longer timeout. The default is 5. See also the ``TIMEOUT`` option of + :command:`execute_process`. + + .. note:: + + In CMake versions 3.10.1 and 3.10.2, this option was called ``TIMEOUT``. + This clashed with the ``TIMEOUT`` test property, which is one of the + common properties that would be set with the ``PROPERTIES`` keyword, + usually leading to legal but unintended behavior. The keyword was + changed to ``DISCOVERY_TIMEOUT`` in CMake 3.10.3 to address this + problem. The ambiguous behavior of the ``TIMEOUT`` keyword in 3.10.1 + and 3.10.2 has not been preserved. + + ``XML_OUTPUT_DIR dir`` + .. versionadded:: 3.18 + + If specified, the parameter is passed along with ``--gtest_output=xml:`` + to test executable. The actual file name is the same as the test target, + including prefix and suffix. This should be used instead of + ``EXTRA_ARGS --gtest_output=xml`` to avoid race conditions writing the + XML result output when using parallel test execution. + + ``DISCOVERY_MODE`` + .. versionadded:: 3.18 + + Provides greater control over when ``gtest_discover_tests()`` performs test + discovery. By default, ``POST_BUILD`` sets up a post-build command + to perform test discovery at build time. In certain scenarios, like + cross-compiling, this ``POST_BUILD`` behavior is not desirable. + By contrast, ``PRE_TEST`` delays test discovery until just prior to test + execution. This way test discovery occurs in the target environment + where the test has a better chance at finding appropriate runtime + dependencies. + + ``DISCOVERY_MODE`` defaults to the value of the + ``CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not + passed when calling ``gtest_discover_tests()``. This provides a mechanism + for globally selecting a preferred test discovery behavior without having + to modify each call site. + +#]=======================================================================] + +# Save project's policies +cmake_policy(PUSH) +cmake_policy(SET CMP0057 NEW) # if IN_LIST + +#------------------------------------------------------------------------------ +function(gtest_add_tests) + + if (ARGC LESS 1) + message(FATAL_ERROR "No arguments supplied to gtest_add_tests()") + endif() + + set(options + SKIP_DEPENDENCY + ) + set(oneValueArgs + TARGET + WORKING_DIRECTORY + TEST_PREFIX + TEST_SUFFIX + TEST_LIST + ) + set(multiValueArgs + SOURCES + EXTRA_ARGS + ) + set(allKeywords ${options} ${oneValueArgs} ${multiValueArgs}) + + unset(sources) + if("${ARGV0}" IN_LIST allKeywords) + cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(autoAddSources YES) + else() + # Non-keyword syntax, convert to keyword form + if (ARGC LESS 3) + message(FATAL_ERROR "gtest_add_tests() without keyword options requires at least 3 arguments") + endif() + set(ARGS_TARGET "${ARGV0}") + set(ARGS_EXTRA_ARGS "${ARGV1}") + if(NOT "${ARGV2}" STREQUAL "AUTO") + set(ARGS_SOURCES "${ARGV}") + list(REMOVE_AT ARGS_SOURCES 0 1) + endif() + endif() + + # The non-keyword syntax allows the first argument to be an arbitrary + # executable rather than a target if source files are also provided. In all + # other cases, both forms require a target. + if(NOT TARGET "${ARGS_TARGET}" AND NOT ARGS_SOURCES) + message(FATAL_ERROR "${ARGS_TARGET} does not define an existing CMake target") + endif() + if(NOT ARGS_WORKING_DIRECTORY) + unset(workDir) + else() + set(workDir WORKING_DIRECTORY "${ARGS_WORKING_DIRECTORY}") + endif() + + if(NOT ARGS_SOURCES) + get_property(ARGS_SOURCES TARGET ${ARGS_TARGET} PROPERTY SOURCES) + endif() + + unset(testList) + + set(gtest_case_name_regex ".*\\( *([A-Za-z_0-9]+) *, *([A-Za-z_0-9]+) *\\).*") + set(gtest_test_type_regex "(TYPED_TEST|TEST_?[FP]?)") + + foreach(source IN LISTS ARGS_SOURCES) + if(NOT ARGS_SKIP_DEPENDENCY) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${source}) + endif() + file(READ "${source}" contents) + string(REGEX MATCHALL "${gtest_test_type_regex} *\\(([A-Za-z_0-9 ,]+)\\)" found_tests "${contents}") + foreach(hit ${found_tests}) + string(REGEX MATCH "${gtest_test_type_regex}" test_type ${hit}) + + # Parameterized tests have a different signature for the filter + if("x${test_type}" STREQUAL "xTEST_P") + string(REGEX REPLACE ${gtest_case_name_regex} "*/\\1.\\2/*" gtest_test_name ${hit}) + elseif("x${test_type}" STREQUAL "xTEST_F" OR "x${test_type}" STREQUAL "xTEST") + string(REGEX REPLACE ${gtest_case_name_regex} "\\1.\\2" gtest_test_name ${hit}) + elseif("x${test_type}" STREQUAL "xTYPED_TEST") + string(REGEX REPLACE ${gtest_case_name_regex} "\\1/*.\\2" gtest_test_name ${hit}) + else() + message(WARNING "Could not parse GTest ${hit} for adding to CTest.") + continue() + endif() + + # Make sure tests disabled in GTest get disabled in CTest + if(gtest_test_name MATCHES "(^|\\.)DISABLED_") + # Add the disabled test if CMake is new enough + # Note that this check is to allow backwards compatibility so this + # module can be copied locally in projects to use with older CMake + # versions + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.8.20170401) + string(REGEX REPLACE + "(^|\\.)DISABLED_" "\\1" + orig_test_name "${gtest_test_name}" + ) + set(ctest_test_name + ${ARGS_TEST_PREFIX}${orig_test_name}${ARGS_TEST_SUFFIX} + ) + add_test(NAME ${ctest_test_name} + ${workDir} + COMMAND ${ARGS_TARGET} + --gtest_also_run_disabled_tests + --gtest_filter=${gtest_test_name} + ${ARGS_EXTRA_ARGS} + ) + set_tests_properties(${ctest_test_name} PROPERTIES DISABLED TRUE) + list(APPEND testList ${ctest_test_name}) + endif() + else() + set(ctest_test_name ${ARGS_TEST_PREFIX}${gtest_test_name}${ARGS_TEST_SUFFIX}) + add_test(NAME ${ctest_test_name} + ${workDir} + COMMAND ${ARGS_TARGET} + --gtest_filter=${gtest_test_name} + ${ARGS_EXTRA_ARGS} + ) + list(APPEND testList ${ctest_test_name}) + endif() + endforeach() + endforeach() + + if(ARGS_TEST_LIST) + set(${ARGS_TEST_LIST} ${testList} PARENT_SCOPE) + endif() + +endfunction() + +#------------------------------------------------------------------------------ + +function(gtest_discover_tests TARGET) + cmake_parse_arguments( + "" + "NO_PRETTY_TYPES;NO_PRETTY_VALUES" + "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;DISCOVERY_TIMEOUT;XML_OUTPUT_DIR;DISCOVERY_MODE" + "EXTRA_ARGS;PROPERTIES" + ${ARGN} + ) + + if(NOT _WORKING_DIRECTORY) + set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + endif() + if(NOT _TEST_LIST) + set(_TEST_LIST ${TARGET}_TESTS) + endif() + if(NOT _DISCOVERY_TIMEOUT) + set(_DISCOVERY_TIMEOUT 5) + endif() + if(NOT _DISCOVERY_MODE) + if(NOT CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE) + set(CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE "POST_BUILD") + endif() + set(_DISCOVERY_MODE ${CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE}) + endif() + + get_property( + has_counter + TARGET ${TARGET} + PROPERTY CTEST_DISCOVERED_TEST_COUNTER + SET + ) + if(has_counter) + get_property( + counter + TARGET ${TARGET} + PROPERTY CTEST_DISCOVERED_TEST_COUNTER + ) + math(EXPR counter "${counter} + 1") + else() + set(counter 1) + endif() + set_property( + TARGET ${TARGET} + PROPERTY CTEST_DISCOVERED_TEST_COUNTER + ${counter} + ) + + # Define rule to generate test list for aforementioned test executable + set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}[${counter}]") + set(ctest_include_file "${ctest_file_base}_include.cmake") + set(ctest_tests_file "${ctest_file_base}_tests.cmake") + get_property(crosscompiling_emulator + TARGET ${TARGET} + PROPERTY CROSSCOMPILING_EMULATOR + ) + + if(_DISCOVERY_MODE STREQUAL "POST_BUILD") + add_custom_command( + TARGET ${TARGET} POST_BUILD + BYPRODUCTS "${ctest_tests_file}" + COMMAND "${CMAKE_COMMAND}" + -D "TEST_TARGET=${TARGET}" + -D "TEST_EXECUTABLE=$" + -D "TEST_EXECUTOR=${crosscompiling_emulator}" + -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}" + -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}" + -D "TEST_PROPERTIES=${_PROPERTIES}" + -D "TEST_PREFIX=${_TEST_PREFIX}" + -D "TEST_SUFFIX=${_TEST_SUFFIX}" + -D "NO_PRETTY_TYPES=${_NO_PRETTY_TYPES}" + -D "NO_PRETTY_VALUES=${_NO_PRETTY_VALUES}" + -D "TEST_LIST=${_TEST_LIST}" + -D "CTEST_FILE=${ctest_tests_file}" + -D "TEST_DISCOVERY_TIMEOUT=${_DISCOVERY_TIMEOUT}" + -D "TEST_XML_OUTPUT_DIR=${_XML_OUTPUT_DIR}" + -P "${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}" + VERBATIM + ) + + file(WRITE "${ctest_include_file}" + "if(EXISTS \"${ctest_tests_file}\")\n" + " include(\"${ctest_tests_file}\")\n" + "else()\n" + " add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)\n" + "endif()\n" + ) + elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST") + + get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL + PROPERTY GENERATOR_IS_MULTI_CONFIG + ) + + if(GENERATOR_IS_MULTI_CONFIG) + set(ctest_tests_file "${ctest_file_base}_tests-$.cmake") + endif() + + string(CONCAT ctest_include_content + "if(EXISTS \"$\")" "\n" + " if(\"$\" IS_NEWER_THAN \"${ctest_tests_file}\")" "\n" + " include(\"${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}\")" "\n" + " gtest_discover_tests_impl(" "\n" + " TEST_EXECUTABLE" " [==[" "$" "]==]" "\n" + " TEST_EXECUTOR" " [==[" "${crosscompiling_emulator}" "]==]" "\n" + " TEST_WORKING_DIR" " [==[" "${_WORKING_DIRECTORY}" "]==]" "\n" + " TEST_EXTRA_ARGS" " [==[" "${_EXTRA_ARGS}" "]==]" "\n" + " TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n" + " TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n" + " TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n" + " NO_PRETTY_TYPES" " [==[" "${_NO_PRETTY_TYPES}" "]==]" "\n" + " NO_PRETTY_VALUES" " [==[" "${_NO_PRETTY_VALUES}" "]==]" "\n" + " TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n" + " CTEST_FILE" " [==[" "${ctest_tests_file}" "]==]" "\n" + " TEST_DISCOVERY_TIMEOUT" " [==[" "${_DISCOVERY_TIMEOUT}" "]==]" "\n" + " TEST_XML_OUTPUT_DIR" " [==[" "${_XML_OUTPUT_DIR}" "]==]" "\n" + " )" "\n" + " endif()" "\n" + " include(\"${ctest_tests_file}\")" "\n" + "else()" "\n" + " add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)" "\n" + "endif()" "\n" + ) + + if(GENERATOR_IS_MULTI_CONFIG) + foreach(_config ${CMAKE_CONFIGURATION_TYPES}) + file(GENERATE OUTPUT "${ctest_file_base}_include-${_config}.cmake" CONTENT "${ctest_include_content}" CONDITION $) + endforeach() + file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")") + else() + file(GENERATE OUTPUT "${ctest_file_base}_include.cmake" CONTENT "${ctest_include_content}") + file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include.cmake\")") + endif() + + else() + message(FATAL_ERROR "Unknown DISCOVERY_MODE: ${_DISCOVERY_MODE}") + endif() + + # Add discovered tests to directory TEST_INCLUDE_FILES + set_property(DIRECTORY + APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}" + ) + +endfunction() + +############################################################################### + +set(_GOOGLETEST_DISCOVER_TESTS_SCRIPT + ${CMAKE_CURRENT_LIST_DIR}/GoogleTestAddTests.cmake +) + +# Restore project's policies +cmake_policy(POP) diff --git a/Sofa/framework/Testing/cmake/GoogleTestAddTests.cmake b/Sofa/framework/Testing/cmake/GoogleTestAddTests.cmake new file mode 100644 index 000000000000..0f79c9afc0e3 --- /dev/null +++ b/Sofa/framework/Testing/cmake/GoogleTestAddTests.cmake @@ -0,0 +1,188 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION ${CMAKE_VERSION}) + +# Overwrite possibly existing ${_CTEST_FILE} with empty file +set(flush_tests_MODE WRITE) + +# Flushes script to ${_CTEST_FILE} +macro(flush_script) + file(${flush_tests_MODE} "${_CTEST_FILE}" "${script}") + set(flush_tests_MODE APPEND) + + set(script "") +endmacro() + +# Flushes tests_buffer to tests +macro(flush_tests_buffer) + list(APPEND tests "${tests_buffer}") + set(tests_buffer "") +endmacro() + +macro(add_command NAME) + set(_args "") + foreach(_arg ${ARGN}) + if(_arg MATCHES "[^-./:a-zA-Z0-9_]") + string(APPEND _args " [==[${_arg}]==]") + else() + string(APPEND _args " ${_arg}") + endif() + endforeach() + string(APPEND script "${NAME}(${_args})\n") + string(LENGTH "${script}" _script_len) + if(${_script_len} GREATER "50000") + flush_script() + endif() + # Unsets macro local variables to prevent leakage outside of this macro. + unset(_args) + unset(_script_len) +endmacro() + +function(gtest_discover_tests_impl) + + cmake_parse_arguments( + "" + "" + "NO_PRETTY_TYPES;NO_PRETTY_VALUES;TEST_EXECUTABLE;TEST_WORKING_DIR;TEST_PREFIX;TEST_SUFFIX;TEST_LIST;CTEST_FILE;TEST_DISCOVERY_TIMEOUT;TEST_XML_OUTPUT_DIR" + "TEST_EXTRA_ARGS;TEST_PROPERTIES;TEST_EXECUTOR" + ${ARGN} + ) + + set(prefix "${_TEST_PREFIX}") + set(suffix "${_TEST_SUFFIX}") + set(extra_args ${_TEST_EXTRA_ARGS}) + set(properties ${_TEST_PROPERTIES}) + set(script) + set(suite) + set(tests) + set(tests_buffer) + + # Run test executable to get list of available tests + if(NOT EXISTS "${_TEST_EXECUTABLE}") + message(FATAL_ERROR + "Specified test executable does not exist.\n" + " Path: '${_TEST_EXECUTABLE}'" + ) + endif() + execute_process( + COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" --gtest_list_tests + WORKING_DIRECTORY "${_TEST_WORKING_DIR}" + TIMEOUT ${_TEST_DISCOVERY_TIMEOUT} + OUTPUT_VARIABLE output + RESULT_VARIABLE result + ) + if(NOT ${result} EQUAL 0) + string(REPLACE "\n" "\n " output "${output}") + message(FATAL_ERROR + "Error running test executable.\n" + " Path: '${_TEST_EXECUTABLE}'\n" + " Result: ${result}\n" + " Output:\n" + " ${output}\n" + ) + endif() + + # Preserve semicolon in test-parameters + string(REPLACE [[;]] [[\;]] output "${output}") + string(REPLACE "\n" ";" output "${output}") + + # Parse output + foreach(line ${output}) + # Skip header + if(NOT line MATCHES "gtest_main\\.cc") + # Do we have a module name or a test name? + if(NOT line MATCHES "^ ") + # Module; remove trailing '.' to get just the name... + string(REGEX REPLACE "\\.( *#.*)?" "" suite "${line}") + if(line MATCHES "#" AND NOT _NO_PRETTY_TYPES) + string(REGEX REPLACE "/[0-9]\\.+ +#.*= +" "/" pretty_suite "${line}") + else() + set(pretty_suite "${suite}") + endif() + string(REGEX REPLACE "^DISABLED_" "" pretty_suite "${pretty_suite}") + else() + # Test name; strip spaces and comments to get just the name... + string(REGEX REPLACE " +" "" test "${line}") + if(test MATCHES "#" AND NOT _NO_PRETTY_VALUES) + string(REGEX REPLACE "/[0-9]+#GetParam..=" "/" pretty_test "${test}") + else() + string(REGEX REPLACE "#.*" "" pretty_test "${test}") + endif() + string(REGEX REPLACE "^DISABLED_" "" pretty_test "${pretty_test}") + string(REGEX REPLACE "#.*" "" test "${test}") + if(NOT "${_TEST_XML_OUTPUT_DIR}" STREQUAL "") + set(TEST_XML_OUTPUT_PARAM "--gtest_output=xml:${_TEST_XML_OUTPUT_DIR}/${prefix}${suite}.${test}${suffix}.xml") + else() + unset(TEST_XML_OUTPUT_PARAM) + endif() + + # sanitize test name for further processing downstream + set(testname "${prefix}${pretty_suite}.${pretty_test}${suffix}") + # escape \ + string(REPLACE [[\]] [[\\]] testname "${testname}") + # escape ; + string(REPLACE [[;]] [[\;]] testname "${testname}") + # escape $ + string(REPLACE [[$]] [[\$]] testname "${testname}") + + # ...and add to script + add_command(add_test + "${testname}" + ${_TEST_EXECUTOR} + "${_TEST_EXECUTABLE}" + "--gtest_filter=${suite}.${test}" + "--gtest_also_run_disabled_tests" + ${TEST_XML_OUTPUT_PARAM} + ${extra_args} + ) + if(suite MATCHES "^DISABLED_" OR test MATCHES "^DISABLED_") + add_command(set_tests_properties + "${testname}" + PROPERTIES DISABLED TRUE + ) + endif() + add_command(set_tests_properties + "${testname}" + PROPERTIES + WORKING_DIRECTORY "${_TEST_WORKING_DIR}" + SKIP_REGULAR_EXPRESSION "\\\\[ SKIPPED \\\\]" + ${properties} + ) + list(APPEND tests_buffer "${testname}") + list(LENGTH tests_buffer tests_buffer_length) + if(${tests_buffer_length} GREATER "250") + flush_tests_buffer() + endif() + endif() + endif() + endforeach() + + + # Create a list of all discovered tests, which users may use to e.g. set + # properties on the tests + flush_tests_buffer() + add_command(set ${_TEST_LIST} ${tests}) + + # Write CTest script + flush_script() + +endfunction() + +if(CMAKE_SCRIPT_MODE_FILE) + gtest_discover_tests_impl( + NO_PRETTY_TYPES ${NO_PRETTY_TYPES} + NO_PRETTY_VALUES ${NO_PRETTY_VALUES} + TEST_EXECUTABLE ${TEST_EXECUTABLE} + TEST_EXECUTOR ${TEST_EXECUTOR} + TEST_WORKING_DIR ${TEST_WORKING_DIR} + TEST_PREFIX ${TEST_PREFIX} + TEST_SUFFIX ${TEST_SUFFIX} + TEST_LIST ${TEST_LIST} + CTEST_FILE ${CTEST_FILE} + TEST_DISCOVERY_TIMEOUT ${TEST_DISCOVERY_TIMEOUT} + TEST_XML_OUTPUT_DIR ${TEST_XML_OUTPUT_DIR} + TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS} + TEST_PROPERTIES ${TEST_PROPERTIES} + ) +endif()