From 7f47f20babb936a1b5422d03f79491e6017455db Mon Sep 17 00:00:00 2001 From: "Friedrich W. H. Kossebau" Date: Wed, 21 Apr 2021 14:59:29 +0200 Subject: ECMGenerateExportHeader: add macros for enumerator deprecation warning --- modules/ECMGenerateExportHeader.cmake | 65 ++++++++++++++++++++-- .../consumer/CMakeLists.txt | 11 +++- .../testAPI_DISABLE_DEPRECATED_BEFORE_AND_AT.cmake | 31 ++++++++--- .../consumer/testAPI_NO_DEPRECATED.cmake | 25 +++++++-- .../library/library.hpp | 5 +- 5 files changed, 116 insertions(+), 21 deletions(-) diff --git a/modules/ECMGenerateExportHeader.cmake b/modules/ECMGenerateExportHeader.cmake index 170531e7..c82d82aa 100644 --- a/modules/ECMGenerateExportHeader.cmake +++ b/modules/ECMGenerateExportHeader.cmake @@ -111,6 +111,24 @@ defined by `GenerateExportHeader API into the compiler warning conditions of already released versions. Since 5.71. +``_ENUMERATOR_DEPRECATED_VERSION(major, minor, text)`` + to use to conditionally set a + ``_DEPRECATED`` macro for an enumerator, depending + on the warnings macro flags set (see below). In builds using C++14 standard or earlier, + where enumerator attributes are not yet supported, the macro will always yield an empty string. + Since 5.83. + +``_ENUMERATOR_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text)`` + to use to conditionally set a + ``_DEPRECATED`` macro for an enumerator, depending + on the warnings macro flags set (see below), with ``major`` & ``minor`` + applied for the logic and ``textmajor`` & ``textminor`` for the warnings message. + In builds using C++14 standard or earlier, where enumerator attributes are not yet supported, + the macro will always yield an empty string. + Useful for retroactive tagging of API for the compiler without injecting the + API into the compiler warning conditions of already released versions. + Since 5.83. + ``_ENABLE_DEPRECATED_SINCE(major, minor)`` evaluates to ``TRUE`` or ``FALSE`` depending on the visibility macro flags set (see below). To be used mainly with ``#if``/``#endif`` to mark sections @@ -254,7 +272,7 @@ uses the generated macro ``FOO_BUILD_DEPRECATED_SINCE``, like this: enum Bars { One, #if FOO_BUILD_DEPRECATED_SINCE(5, 0) - Two, + Two FOO_ENUMERATOR_DEPRECATED_VERSION(5, 0, "Use Three"), // macro available since 5.83 #endif Three, }; @@ -531,7 +549,7 @@ function(ecm_generate_export_header target) endif() # for the set of compiler versions supported by ECM/KF we can assume those attributes supported - # KF6: with C++14 support expected, switch to always use [[deprecated(text)]] + # KF6: with C++17 as minimum standard planned, switch to always use [[deprecated(text)]] if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") set(_decl_deprecated_text_definition "__attribute__ ((__deprecated__(text)))") elseif(MSVC) @@ -539,10 +557,23 @@ function(ecm_generate_export_header target) else() set(_decl_deprecated_text_definition "${_macro_base_name}_DECL_DEPRECATED") endif() - # generate header file set(_output " -#define ${_macro_base_name}_DECL_DEPRECATED_TEXT(text) ${_decl_deprecated_text_definition} +#if defined(__has_cpp_attribute) && __has_cpp_attribute(deprecated) +" + ) + # needed below to check if [[deprecated(text)]] is used + if(MSVC) + string(APPEND _output +"# define ECM_GENERATEEXPORTHEADER_HAVE_DEPRECATED_ATTRIBUTE +" + ) + endif() + string(APPEND _output +"# define ${_macro_base_name}_DECL_DEPRECATED_TEXT(text) [[deprecated(text)]] +#else +# define ${_macro_base_name}_DECL_DEPRECATED_TEXT(text) ${_decl_deprecated_text_definition} +#endif #define ECM_GENERATEEXPORTHEADER_VERSION_VALUE(major, minor, patch) ((major<<16)|(minor<<8)|(patch)) " @@ -687,6 +718,32 @@ function(ecm_generate_export_header target) ) string(APPEND _output "#define ${_macro_base_name}_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text) ${_macro_base_name}_DEPRECATED_VERSION_##major(minor, \"Since \"#textmajor\".\"#textminor\". \" text) +" + ) + # reusing the existing version-controlled deprecation macros for enumerator deprecation macros + # to avoid having to repeat all the explicit version variants + # MSVC seems to have issues with __declspec(deprecated) being used as enumerator attribute + # so it needs a separate check to ensure the macro is using [[deprecated(text)]] + # [[deprecated(text)]] is part of C++14 and enumerator attributes are part of C++17, + # so one should assume just checking the latter is enough, but no idea if this can be relied on + if(MSVC) + string(APPEND _output +"#if defined(ECM_GENERATEEXPORTHEADER_HAVE_DEPRECATED_ATTRIBUTE) && defined(__cpp_enumerator_attributes) && __cpp_enumerator_attributes >= 201411 +" + ) + else() + string(APPEND _output +"#if defined(__cpp_enumerator_attributes) && __cpp_enumerator_attributes >= 201411 +" + ) + endif() + string(APPEND _output +"# define ${_macro_base_name}_ENUMERATOR_DEPRECATED_VERSION(major, minor, text) ${_macro_base_name}_DEPRECATED_VERSION(major, minor, text) +# define ${_macro_base_name}_ENUMERATOR_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text) ${_macro_base_name}_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text) +#else +# define ${_macro_base_name}_ENUMERATOR_DEPRECATED_VERSION(major, minor, text) +# define ${_macro_base_name}_ENUMERATOR_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text) +#endif " ) endif() diff --git a/tests/ECMGenerateExportHeaderTest/consumer/CMakeLists.txt b/tests/ECMGenerateExportHeaderTest/consumer/CMakeLists.txt index ba5940cd..ee042969 100644 --- a/tests/ECMGenerateExportHeaderTest/consumer/CMakeLists.txt +++ b/tests/ECMGenerateExportHeaderTest/consumer/CMakeLists.txt @@ -24,7 +24,16 @@ include(testAPI_${TEST_VARIANT}.cmake) # for each API element test their visibility to the compiler and if a warning is emitted set(_code "Enum enumerator = Enumerator_deprecatedAt2_0;") -testAPI(_code DEPRECATED_AT 2.0 BUILD_TIME_ONLY_DISABLABLE NO_WARNING) +testAPI(_code DEPRECATED_AT 2.0 CXX_STANDARD 11 BUILD_TIME_ONLY_DISABLABLE NO_WARNING) + +set(_code "Enum enumerator = Enumerator_deprecatedAt2_0;") +testAPI(_code DEPRECATED_AT 2.0 CXX_STANDARD 17 BUILD_TIME_ONLY_DISABLABLE) + +set(_code "Enum enumerator = Enumerator_deprecatedAt2_12;") +testAPI(_code DEPRECATED_AT 2.12 CXX_STANDARD 11 NO_WARNING) + +set(_code "Enum enumerator = Enumerator_deprecatedAt2_12;") +testAPI(_code DEPRECATED_AT 2.12 CXX_STANDARD 17) set(_code "Enum enumerator = Enumerator_not_deprecated;") testAPI(_code) diff --git a/tests/ECMGenerateExportHeaderTest/consumer/testAPI_DISABLE_DEPRECATED_BEFORE_AND_AT.cmake b/tests/ECMGenerateExportHeaderTest/consumer/testAPI_DISABLE_DEPRECATED_BEFORE_AND_AT.cmake index 70065cdf..df7716a1 100644 --- a/tests/ECMGenerateExportHeaderTest/consumer/testAPI_DISABLE_DEPRECATED_BEFORE_AND_AT.cmake +++ b/tests/ECMGenerateExportHeaderTest/consumer/testAPI_DISABLE_DEPRECATED_BEFORE_AND_AT.cmake @@ -24,7 +24,7 @@ endif() function(testAPI code_var_name) set(options BUILD_TIME_ONLY_DISABLABLE NO_WARNING) - set(oneValueArgs DEPRECATED_AT) + set(oneValueArgs DEPRECATED_AT CXX_STANDARD) set(multiValueArgs) cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -36,6 +36,17 @@ function(testAPI code_var_name) set(_build_result_expected FALSE) endif() + if (ARGS_CXX_STANDARD) + if(MSVC) + set(std_flag "/std:c++${ARGS_CXX_STANDARD}") + else() + set(std_flag "-std=c++${ARGS_CXX_STANDARD}") + endif() + else() + set(std_flag) + endif() + + set(CMAKE_REQUIRED_FLAGS "${std_flag}") set(CMAKE_REQUIRED_LIBRARIES library) set(CMAKE_REQUIRED_DEFINITIONS "-D${_deprecation_macros_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT=${disable_deprecated_before_and_at_hexnumber}") @@ -54,24 +65,26 @@ int main(int, char**) # check warning if(_build_result_expected) - if((ARGS_BUILD_TIME_ONLY_DISABLABLE AND NOT ARGS_NO_WARNING) OR - (NOT ARGS_BUILD_TIME_ONLY_DISABLABLE AND ARGS_DEPRECATED_AT AND - ARGS_DEPRECATED_AT VERSION_GREATER LIBRARY_DISABLE_DEPRECATED_BEFORE_AND_AT)) - set(_dep_warning_result_expected FALSE) + if(NOT ARGS_NO_WARNING AND + ((ARGS_BUILD_TIME_ONLY_DISABLABLE) OR + (NOT ARGS_BUILD_TIME_ONLY_DISABLABLE AND ARGS_DEPRECATED_AT AND + ARGS_DEPRECATED_AT VERSION_GREATER LIBRARY_DISABLE_DEPRECATED_BEFORE_AND_AT))) + set(_dep_warning_as_error_result_expected FALSE) else() - set(_dep_warning_result_expected TRUE) + set(_dep_warning_as_error_result_expected TRUE) endif() if(MSVC) # warning C4996 warns about deprecated declarations - set(CMAKE_REQUIRED_FLAGS "-we4996") + set(dep_warning_as_error_flag "-we4996") else() - set(CMAKE_REQUIRED_FLAGS "-Werror=deprecated-declarations") + set(dep_warning_as_error_flag "-Werror=deprecated-declarations") endif() + set(CMAKE_REQUIRED_FLAGS "${std_flag} ${dep_warning_as_error_flag}") set(CMAKE_REQUIRED_DEFINITIONS) # unset LIBRARY_DISABLE_DEPRECATED_BEFORE_AND_AT, as LIBRARY_DEPRECATED_WARNINGS_SINCE defaults to it unset(_dep_warning_result CACHE) # clear out as check_cxx_source_compiles caches the result check_cxx_source_compiles("${_code}" _dep_warning_result) - assert_var_bool_value(_dep_warning_result ${_dep_warning_result_expected}) + assert_var_bool_value(_dep_warning_result ${_dep_warning_as_error_result_expected}) endif() endfunction() diff --git a/tests/ECMGenerateExportHeaderTest/consumer/testAPI_NO_DEPRECATED.cmake b/tests/ECMGenerateExportHeaderTest/consumer/testAPI_NO_DEPRECATED.cmake index fa6a63ec..b01dfc3d 100644 --- a/tests/ECMGenerateExportHeaderTest/consumer/testAPI_NO_DEPRECATED.cmake +++ b/tests/ECMGenerateExportHeaderTest/consumer/testAPI_NO_DEPRECATED.cmake @@ -6,7 +6,7 @@ endif() function(testAPI code_var_name) set(options BUILD_TIME_ONLY_DISABLABLE NO_WARNING) - set(oneValueArgs DEPRECATED_AT) + set(oneValueArgs DEPRECATED_AT CXX_STANDARD) set(multiValueArgs) cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -17,6 +17,17 @@ function(testAPI code_var_name) set(_build_result_expected FALSE) endif() + if (ARGS_CXX_STANDARD) + if(MSVC) + set(std_flag "/std:c++${ARGS_CXX_STANDARD}") + else() + set(std_flag "-std=c++${ARGS_CXX_STANDARD}") + endif() + else() + set(std_flag) + endif() + + set(CMAKE_REQUIRED_FLAGS "${std_flag}") set(CMAKE_REQUIRED_LIBRARIES library) set(CMAKE_REQUIRED_DEFINITIONS "-D${_deprecation_macros_base_name}_NO_DEPRECATED") @@ -36,20 +47,22 @@ int main(int, char**) # check warning if(_build_result_expected) if(ARGS_BUILD_TIME_ONLY_DISABLABLE AND NOT ARGS_NO_WARNING) - set(_dep_warning_result_expected FALSE) + set(_dep_warning_as_error_result_expected FALSE) else() - set(_dep_warning_result_expected TRUE) + set(_dep_warning_as_error_result_expected TRUE) endif() if(MSVC) # warning C4996 warns about deprecated declarations - set(CMAKE_REQUIRED_FLAGS "-we4996") + set(dep_warning_as_error_flag "-we4996") else() - set(CMAKE_REQUIRED_FLAGS "-Werror=deprecated-declarations") + set(dep_warning_as_error_flag "-Werror=deprecated-declarations") endif() + + set(CMAKE_REQUIRED_FLAGS "${std_flag} ${dep_warning_as_error_flag}") set(CMAKE_REQUIRED_DEFINITIONS) # unset LIBRARY_DISABLE_DEPRECATED_BEFORE_AND_AT, as LIBRARY_DEPRECATED_WARNINGS_SINCE defaults to it unset(_dep_warning_result CACHE) # clear out as check_cxx_source_compiles caches the result check_cxx_source_compiles("${_code}" _dep_warning_result) - assert_var_bool_value(_dep_warning_result ${_dep_warning_result_expected}) + assert_var_bool_value(_dep_warning_result ${_dep_warning_as_error_result_expected}) endif() endfunction() diff --git a/tests/ECMGenerateExportHeaderTest/library/library.hpp b/tests/ECMGenerateExportHeaderTest/library/library.hpp index 2124bf8a..4afe87ac 100644 --- a/tests/ECMGenerateExportHeaderTest/library/library.hpp +++ b/tests/ECMGenerateExportHeaderTest/library/library.hpp @@ -2,9 +2,12 @@ enum Enum { #if LIBRARY_BUILD_DEPRECATED_SINCE(2, 0) - Enumerator_deprecatedAt2_0, + Enumerator_deprecatedAt2_0 LIBRARY_ENUMERATOR_DEPRECATED_VERSION(2, 0, "Deprecated at 2.0"), #endif Enumerator_not_deprecated, +#if LIBRARY_ENABLE_DEPRECATED_SINCE(2, 12) + Enumerator_deprecatedAt2_12 LIBRARY_ENUMERATOR_DEPRECATED_VERSION(2, 12, "Deprecated at 2.12"), +#endif }; #if LIBRARY_ENABLE_DEPRECATED_SINCE(2, 0) -- cgit v1.2.1