From b350f95ecfcac7635bdbbc12a00a872e08b66829 Mon Sep 17 00:00:00 2001 From: Marc Chevrier Date: Sat, 21 Dec 2024 17:32:51 +0100 Subject: [PATCH] LINK_WARNING_AS_ERROR property: extend capabilities Fixes: #26536 --- Help/prop_tgt/LINK_WARNING_AS_ERROR.rst | 14 +++++ Source/cmLocalGenerator.cxx | 54 +++++++++++++++--- Tests/RunCMake/CMakeLists.txt | 3 +- .../LinkWarningAsError/BadValue-result.txt | 1 + .../LinkWarningAsError/BadValue-stderr.txt | 4 ++ .../LinkWarningAsError/BadValue.cmake | 4 ++ .../LinkWarningAsError/RunCMakeTest.cmake | 4 ++ .../WarnError-validation.cmake | 11 ++-- .../LinkWarningAsError/WarnError.cmake | 57 +++++++++++++++++-- .../WarnErrorOn3-build-check.cmake | 4 ++ .../LinkWarningAsError/WarnErrorOn3.cmake | 5 ++ .../WarnErrorOn4-build-check.cmake | 4 ++ .../LinkWarningAsError/WarnErrorOn4.cmake | 5 ++ 13 files changed, 152 insertions(+), 18 deletions(-) create mode 100644 Tests/RunCMake/LinkWarningAsError/BadValue-result.txt create mode 100644 Tests/RunCMake/LinkWarningAsError/BadValue-stderr.txt create mode 100644 Tests/RunCMake/LinkWarningAsError/BadValue.cmake create mode 100644 Tests/RunCMake/LinkWarningAsError/WarnErrorOn3-build-check.cmake create mode 100644 Tests/RunCMake/LinkWarningAsError/WarnErrorOn3.cmake create mode 100644 Tests/RunCMake/LinkWarningAsError/WarnErrorOn4-build-check.cmake create mode 100644 Tests/RunCMake/LinkWarningAsError/WarnErrorOn4.cmake diff --git a/Help/prop_tgt/LINK_WARNING_AS_ERROR.rst b/Help/prop_tgt/LINK_WARNING_AS_ERROR.rst index 61b78461c1..3c065add94 100644 --- a/Help/prop_tgt/LINK_WARNING_AS_ERROR.rst +++ b/Help/prop_tgt/LINK_WARNING_AS_ERROR.rst @@ -8,6 +8,20 @@ If enabled, adds a flag to treat warnings on link as errors. If the :option:`cmake --link-no-warning-as-error` option is given on the :manual:`cmake(1)` command line, this property is ignored. +This property takes a :ref:`semicolon-separated-list ` of +the following values: + +* ``LINKER``: treat the linker warnings as errors. +* ``DRIVER``: treat the compiler warnings as errors when used to drive the link + step. See the :prop_tgt:`COMPILE_WARNING_AS_ERROR` target property for more + information. + +Moreover, for consistency with the :prop_tgt:`COMPILE_WARNING_AS_ERROR` target +property, a boolean value can be specified: + +* ``True`` value: this is equivalent to ``LINKER`` and ``DRIVER`` values. +* ``False`` value: deactivate this feature for the target. + This property is not implemented for all linkers. It is silently ignored if there is no implementation for the linker being used. The currently implemented :variable:`compiler linker IDs _COMPILER_LINKER_ID>` diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 0197e339c9..1f30e9fbaf 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -3608,14 +3608,54 @@ void cmLocalGenerator::AppendWarningAsErrorLinkerFlags( } const auto wError = target->GetProperty("LINK_WARNING_AS_ERROR"); - const auto wErrorOpts = this->Makefile->GetDefinition( - cmStrCat("CMAKE_", lang, "_LINK_OPTIONS_WARNING_AS_ERROR")); - if (wError.IsOn() && wErrorOpts.IsSet()) { - auto items = cmExpandListWithBacktrace(wErrorOpts, target->GetBacktrace()); - target->ResolveLinkerWrapper(items, lang); - for (const auto& item : items) { - this->AppendFlagEscape(flags, item.Value); + if (wError.IsOff()) { + return; + } + cmList wErrorOptions; + if (wError.IsOn()) { + wErrorOptions = { "DRIVER", "LINKER" }; + } else { + wErrorOptions = wError; + std::sort(wErrorOptions.begin(), wErrorOptions.end()); + wErrorOptions.erase( + std::unique(wErrorOptions.begin(), wErrorOptions.end()), + wErrorOptions.end()); + } + + auto linkModeIsDriver = + this->Makefile->GetDefinition(cmStrCat("CMAKE_", lang, "_LINK_MODE")) == + "DRIVER"_s; + std::string errorMessage; + for (const auto& option : wErrorOptions) { + if (option != "DRIVER"_s && option != "LINKER"_s) { + errorMessage += cmStrCat(" ", option, '\n'); + continue; } + + if (option == "DRIVER"_s && !linkModeIsDriver) { + continue; + } + + const auto wErrorOpts = this->Makefile->GetDefinition(cmStrCat( + "CMAKE_", lang, '_', (option == "DRIVER"_s ? "COMPILE" : "LINK"), + "_OPTIONS_WARNING_AS_ERROR")); + if (wErrorOpts.IsSet()) { + auto items = + cmExpandListWithBacktrace(wErrorOpts, target->GetBacktrace()); + if (option == "LINKER"_s) { + target->ResolveLinkerWrapper(items, lang); + } + for (const auto& item : items) { + this->AppendFlagEscape(flags, item.Value); + } + } + } + if (!errorMessage.empty()) { + this->Makefile->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "Erroneous value(s) for 'LINK_WARNING_AS_ERROR' property of target '", + target->GetName(), "':\n", errorMessage)); } } diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 92d258de1b..30113f1c20 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -508,10 +508,11 @@ add_RunCMake_test(ToolchainFile) add_RunCMake_test(find_dependency) add_RunCMake_test(CompileDefinitions) add_RunCMake_test(CompileWarningAsError -DCMake_TEST_CUDA=${CMake_TEST_CUDA}) -if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|MSVC" +if((CMAKE_C_COMPILER_ID MATCHES "AppleClang|MSVC" OR (CMAKE_SYSTEM_NAME MATCHES "Linux|Windows" AND CMAKE_C_COMPILER_ID MATCHES "Clang|GNU") OR (CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND CMAKE_C_COMPILER_ID STREQUAL "SunPro") OR (CMAKE_SYSTEM_NAME STREQUAL "AIX" AND CMAKE_C_COMPILER_ID STREQUAL "XL")) + AND CMAKE_C_COMPILER_LINKER_ID) add_RunCMake_test(LinkWarningAsError) endif() set_property(TEST RunCMake.CompileWarningAsError APPEND PROPERTY LABELS "CUDA") diff --git a/Tests/RunCMake/LinkWarningAsError/BadValue-result.txt b/Tests/RunCMake/LinkWarningAsError/BadValue-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/LinkWarningAsError/BadValue-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/LinkWarningAsError/BadValue-stderr.txt b/Tests/RunCMake/LinkWarningAsError/BadValue-stderr.txt new file mode 100644 index 0000000000..78a5a455a4 --- /dev/null +++ b/Tests/RunCMake/LinkWarningAsError/BadValue-stderr.txt @@ -0,0 +1,4 @@ +CMake Error: + Erroneous value\(s\) for 'LINK_WARNING_AS_ERROR' property of target 'main': + + FOO diff --git a/Tests/RunCMake/LinkWarningAsError/BadValue.cmake b/Tests/RunCMake/LinkWarningAsError/BadValue.cmake new file mode 100644 index 0000000000..46441fd2db --- /dev/null +++ b/Tests/RunCMake/LinkWarningAsError/BadValue.cmake @@ -0,0 +1,4 @@ +enable_language(C) + +add_executable(main main.c) +set_property(TARGET main PROPERTY LINK_WARNING_AS_ERROR FOO) diff --git a/Tests/RunCMake/LinkWarningAsError/RunCMakeTest.cmake b/Tests/RunCMake/LinkWarningAsError/RunCMakeTest.cmake index cc81ec60cf..16b72dd8fe 100644 --- a/Tests/RunCMake/LinkWarningAsError/RunCMakeTest.cmake +++ b/Tests/RunCMake/LinkWarningAsError/RunCMakeTest.cmake @@ -1,5 +1,7 @@ include(RunCMake) +run_cmake(BadValue) + function(run_link_warn test) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) set(RunCMake_TEST_OUTPUT_MERGE 1) @@ -14,6 +16,8 @@ endfunction() run_link_warn(WarnErrorOn1) run_link_warn(WarnErrorOn2) +run_link_warn(WarnErrorOn3) +run_link_warn(WarnErrorOn4) run_link_warn(WarnErrorOff1) run_link_warn(WarnErrorOff2) run_link_warn(WarnErrorOnIgnore "--link-no-warning-as-error") diff --git a/Tests/RunCMake/LinkWarningAsError/WarnError-validation.cmake b/Tests/RunCMake/LinkWarningAsError/WarnError-validation.cmake index 3b8c55fcaf..9f2c8df479 100644 --- a/Tests/RunCMake/LinkWarningAsError/WarnError-validation.cmake +++ b/Tests/RunCMake/LinkWarningAsError/WarnError-validation.cmake @@ -4,17 +4,16 @@ if (NOT EXISTS "${reference_file}") set (RunCMake_TEST_FAILED "${reference_file}: Reference file not found.") return() endif() -file(READ "${reference_file}" linker_WarnError) +file(READ "${reference_file}" WarnErrorFlags) -if(NOT linker_WarnError STREQUAL "UNDEFINED") +if(NOT WarnErrorFlags STREQUAL "UNDEFINED") if(WARNING_AS_ERROR) - # Add regex [^-] to avoid matching of MSVC compiler flag /WX- - if(NOT actual_stdout MATCHES "${linker_WarnError}[^-]") + if(NOT actual_stdout MATCHES "${WarnErrorFlags}") set (RunCMake_TEST_FAILED "LINK_WARNING_AS_ERROR: flag is missing.") endif() else() - if(actual_stdout MATCHES "${linker_WarnError}[^-]") - set (RunCMake_TEST_FAILED "LINK_WARNING_AS_ERROR: flag unexpectedly present.") + if(actual_stdout MATCHES "${WarnErrorFlags}") + set (RunCMake_TEST_FAILED "LINK_WARNING_AS_ERROR: flag unexpectedly present: '${WarnErrorFlags}'") endif() endif() endif() diff --git a/Tests/RunCMake/LinkWarningAsError/WarnError.cmake b/Tests/RunCMake/LinkWarningAsError/WarnError.cmake index e4818bff4a..91d3d68cf7 100644 --- a/Tests/RunCMake/LinkWarningAsError/WarnError.cmake +++ b/Tests/RunCMake/LinkWarningAsError/WarnError.cmake @@ -1,6 +1,17 @@ -if(NOT CMAKE_C_LINK_OPTIONS_WARNING_AS_ERROR) - set(linker_WarnError "UNDEFINED") +set(linkWarning "${link_warning_as_error}") +if (DEFINED CMAKE_LINK_WARNING_AS_ERROR) + set(linkWarning "${CMAKE_LINK_WARNING_AS_ERROR}") +endif() +if (linkWarning STREQUAL "ON") + set (linkWarning DRIVER LINKER) +endif() + +if((linkWarning STREQUAL "DRIVER;LINKER" AND NOT CMAKE_C_COMPILE_OPTIONS_WARNING_AS_ERROR + AND NOT CMAKE_C_LINK_OPTIONS_WARNING_AS_ERROR) + OR (linkWarning STREQUAL "DRIVER" AND NOT CMAKE_C_COMPILE_OPTIONS_WARNING_AS_ERROR) + OR (linkWarning STREQUAL "LINKER" AND NOT CMAKE_C_LINK_OPTIONS_WARNING_AS_ERROR)) + set(WarnErrorFlags "UNDEFINED") else() set(cfg_dir) get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) @@ -26,8 +37,15 @@ else() add_dependencies(main dump) - # generate reference for WARNING_AS_ERROR flag + unset(compiler_WarnError) + unset(linker_WarnError) + unset(WarnErrorFlags) + ## DRIVER + if (CMAKE_C_LINK_MODE STREQUAL "DRIVER") + list(JOIN CMAKE_C_COMPILE_OPTIONS_WARNING_AS_ERROR " " compiler_WarnError) + endif() + ## LINKER string(REPLACE "LINKER:" "" linker_WarnError "${CMAKE_C_LINK_OPTIONS_WARNING_AS_ERROR}") if (CMAKE_C_LINKER_WRAPPER_FLAG) set(linker_flag ${CMAKE_C_LINKER_WRAPPER_FLAG}) @@ -50,5 +68,36 @@ else() else() string(REPLACE "," " " linker_WarnError "${linker_WarnError}") endif() + + # Add regex [^-] to avoid matching of MSVC compiler flag -WX- + if(linkWarning STREQUAL "DRIVER;LINKER") + set(WarnErrorFlags "${compiler_WarnError}") + if (WarnErrorFlags) + string(APPEND WarnErrorFlags " ${linker_WarnError}[^-]") + else() + set(WarnErrorFlags "${linker_WarnError}[^-]") + endif() + elseif(linkWarning STREQUAL "DRIVER") + set(WarnErrorFlags "${compiler_WarnError}[^-]") + elseif(linkWarning STREQUAL "LINKER") + set(WarnErrorFlags "${linker_WarnError}[^-]") + else() + # OFF value + if(compiler_WarnError AND linker_WarnError) + set(WarnErrorFlags "(${compiler_WarnError}[^-]|${linker_WarnError}[^-])+") + elseif(compiler_WarnError) + set(WarnErrorFlags "${compiler_WarnError}[^-]") + elseif(linker_WarnError) + set(WarnErrorFlags "${linker_WarnError}[^-]") + endif() + if(NOT WarnErrorFlags) + set(WarnErrorFlags "UNDEFINED") + endif() + endif() endif() -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/WARNING_AS_ERROR.txt" "${linker_WarnError}") +if(CMAKE_GENERATOR MATCHES "Visual Studio") + # replace '-' with '/' for options + string(REGEX REPLACE "-([A-Z]+)" "[-/]\\1" WarnErrorFlags "${WarnErrorFlags}") +endif() + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/WARNING_AS_ERROR.txt" "${WarnErrorFlags}") diff --git a/Tests/RunCMake/LinkWarningAsError/WarnErrorOn3-build-check.cmake b/Tests/RunCMake/LinkWarningAsError/WarnErrorOn3-build-check.cmake new file mode 100644 index 0000000000..c395cdadbe --- /dev/null +++ b/Tests/RunCMake/LinkWarningAsError/WarnErrorOn3-build-check.cmake @@ -0,0 +1,4 @@ + +set(WARNING_AS_ERROR ON) + +include("${CMAKE_CURRENT_LIST_DIR}/WarnError-validation.cmake") diff --git a/Tests/RunCMake/LinkWarningAsError/WarnErrorOn3.cmake b/Tests/RunCMake/LinkWarningAsError/WarnErrorOn3.cmake new file mode 100644 index 0000000000..6ec5a1e67a --- /dev/null +++ b/Tests/RunCMake/LinkWarningAsError/WarnErrorOn3.cmake @@ -0,0 +1,5 @@ +enable_language(C) + +set(link_warning_as_error DRIVER) + +include(WarnError.cmake) diff --git a/Tests/RunCMake/LinkWarningAsError/WarnErrorOn4-build-check.cmake b/Tests/RunCMake/LinkWarningAsError/WarnErrorOn4-build-check.cmake new file mode 100644 index 0000000000..c395cdadbe --- /dev/null +++ b/Tests/RunCMake/LinkWarningAsError/WarnErrorOn4-build-check.cmake @@ -0,0 +1,4 @@ + +set(WARNING_AS_ERROR ON) + +include("${CMAKE_CURRENT_LIST_DIR}/WarnError-validation.cmake") diff --git a/Tests/RunCMake/LinkWarningAsError/WarnErrorOn4.cmake b/Tests/RunCMake/LinkWarningAsError/WarnErrorOn4.cmake new file mode 100644 index 0000000000..13c89e6901 --- /dev/null +++ b/Tests/RunCMake/LinkWarningAsError/WarnErrorOn4.cmake @@ -0,0 +1,5 @@ +enable_language(C) + +set(link_warning_as_error LINKER) + +include(WarnError.cmake)