From 5abaf65154d477b06e544dc587c3b544db4016a2 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Wed, 24 Jun 2026 12:18:54 -0400 Subject: [PATCH] Check*: Restore toleration of `;-W...` in CMAKE_REQUIRED_FLAGS The `CMAKE_REQUIRED_FLAGS` variable, used by the various entry points to `cmake_check_source_{compiles,runs}`, specifies that flags shall not be separated by semicolons. This is because the way in which it is passed to the child `cmake` process means that, when considered as a list (with semicolon separated items), only the first item is actually used as compile flags, with any additional items actually seen as arguments to the child `cmake` process. While this usage is unsupported, if invoked accidentally, flags starting with `-W` were silently ignored by CMake. However, CMake 4.4 newly complains about unrecognized warning categories, which means that uses which were silently broken now result in a hard error. In the longer term, it would be helpful to introduce a policy to repair improper use of semicolons in `CMAKE_REQUIRED_FLAGS` in order to do what the user probably meant. However, for various reasons, we do not want to introduce a policy to CMake 4.4 at this time, and anyway, the policy won't help users that haven't enabled it. Therefore, alter the 'check source' functions to detect arguments being passed to `cmake` rather than the compiler, and remove any such that start with `-W`. This removes any arguments that newly cause errors starting with CMake 4.4, while not otherwise altering behavior in case any users were abusing this implementation quirk. Strictly speaking, this means that users can no longer abuse this quirk to alter CMake's diagnostics. However, the only case in which this should be observable is if the user is already abusing the quirk to inject their own CMake logic into the CMake-provided test projects. Since this was never supported, anyone doing that can keep the pieces. Issue: #27901 Fixes: #27893 --- Modules/Internal/CheckSourceCompiles.cmake | 38 ++++++++++++++++++- Modules/Internal/CheckSourceRuns.cmake | 38 ++++++++++++++++++- .../RunCMake/CheckSourceCompiles/Extra.cmake | 1 + .../CheckSourceCompiles/Issue27893.cmake | 9 +++++ .../RequiredFlags-stderr.txt | 7 ++++ .../CheckSourceCompiles/RequiredFlags.cmake | 17 +++++++++ .../CheckSourceCompiles/RunCMakeTest.cmake | 3 ++ Tests/RunCMake/CheckSourceRuns/Extra.cmake | 1 + .../RunCMake/CheckSourceRuns/Issue27893.cmake | 9 +++++ .../CheckSourceRuns/RequiredFlags-stderr.txt | 7 ++++ .../CheckSourceRuns/RequiredFlags.cmake | 17 +++++++++ .../CheckSourceRuns/RunCMakeTest.cmake | 3 ++ 12 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 Tests/RunCMake/CheckSourceCompiles/Extra.cmake create mode 100644 Tests/RunCMake/CheckSourceCompiles/Issue27893.cmake create mode 100644 Tests/RunCMake/CheckSourceCompiles/RequiredFlags-stderr.txt create mode 100644 Tests/RunCMake/CheckSourceCompiles/RequiredFlags.cmake create mode 100644 Tests/RunCMake/CheckSourceRuns/Extra.cmake create mode 100644 Tests/RunCMake/CheckSourceRuns/Issue27893.cmake create mode 100644 Tests/RunCMake/CheckSourceRuns/RequiredFlags-stderr.txt create mode 100644 Tests/RunCMake/CheckSourceRuns/RequiredFlags.cmake diff --git a/Modules/Internal/CheckSourceCompiles.cmake b/Modules/Internal/CheckSourceCompiles.cmake index e5c0ef12de..18ee7d17ae 100644 --- a/Modules/Internal/CheckSourceCompiles.cmake +++ b/Modules/Internal/CheckSourceCompiles.cmake @@ -97,6 +97,40 @@ function(CMAKE_CHECK_SOURCE_COMPILES _lang _source _var) set(CHECK_${LANG}_SOURCE_COMPILES_ADD_INCLUDES) endif() + set(_CSC_EXTRA_CMAKE_ARGUMENTS) + string(REPLACE "\\;" "\\\\;" CMAKE_REQUIRED_FLAGS + "${CMAKE_REQUIRED_FLAGS}") + #[[ + cmake_polify(GET CMP0999 _CSC_CMP0999) + if(_CSC_CMP0999 STREQUAL "NEW") + # Join multiple list arguments into the space-separated string that we + # want. This ensures that the entirety of the value gets passed as + # compile arguments, which is what the user almost surely intended. + # + # FIXME(#27901): This needs a policy to be implemented. This will be + # available in a future version of CMake. + list(JOIN CMAKE_REQUIRED_FLAGS " " CMAKE_REQUIRED_FLAGS) + else() + #]] + # If CMAKE_REQUIRED_FLAGS contains an unescaped semicolon, anything after + # gets passed as flags to 'cmake' itself. This is probably not intended, + # but we preserve it for compatibility. + set(_CSC_EXTRA_CMAKE_ARGUMENTS "${CMAKE_REQUIRED_FLAGS}") + list(POP_FRONT _CSC_EXTRA_CMAKE_ARGUMENTS CMAKE_REQUIRED_FLAGS) + #[[ + if(NOT CMAKE_REQUIRED_FLAGS STREQUAL "") + cmake_policy(ISSUE_WARNING CMP0999) TODO + endif() + #]] + # There is at least one known instance of users accidentally passing + # '-W' arguments intended for the compiler as arguments to CMake. Since + # CMake complains about unknown '-W' arguments starting with CMake 4.4, + # we need to strip these for compatibility. + string(REPLACE "\\;" "\\\\;" _CSC_EXTRA_CMAKE_ARGUMENTS + "${_CSC_EXTRA_CMAKE_ARGUMENTS}") + list(FILTER _CSC_EXTRA_CMAKE_ARGUMENTS EXCLUDE REGEX "^-W") + #endif() + if(NOT CMAKE_REQUIRED_QUIET) message(CHECK_START "Performing Test ${_var}") endif() @@ -106,7 +140,9 @@ function(CMAKE_CHECK_SOURCE_COMPILES _lang _source _var) COMPILE_DEFINITIONS -D${_var} ${CMAKE_REQUIRED_DEFINITIONS} ${CHECK_${LANG}_SOURCE_COMPILES_ADD_LINK_OPTIONS} ${CHECK_${LANG}_SOURCE_COMPILES_ADD_LIBRARIES} - CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} + CMAKE_FLAGS + -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} + ${_CSC_EXTRA_CMAKE_ARGUMENTS} "${CHECK_${LANG}_SOURCE_COMPILES_ADD_INCLUDES}" "${_CSC_LINK_DIRECTORIES}" OUTPUT_VARIABLE OUTPUT) diff --git a/Modules/Internal/CheckSourceRuns.cmake b/Modules/Internal/CheckSourceRuns.cmake index 5df37e0710..a698e867ce 100644 --- a/Modules/Internal/CheckSourceRuns.cmake +++ b/Modules/Internal/CheckSourceRuns.cmake @@ -88,6 +88,40 @@ function(CMAKE_CHECK_SOURCE_RUNS _lang _source _var) set(CHECK_${_lang}_SOURCE_COMPILES_ADD_INCLUDES) endif() + set(_CSR_EXTRA_CMAKE_ARGUMENTS) + string(REPLACE "\\;" "\\\\;" CMAKE_REQUIRED_FLAGS + "${CMAKE_REQUIRED_FLAGS}") + #[[ + cmake_polify(GET CMP0999 _CSR_CMP0999) + if(_CSR_CMP0999 STREQUAL "NEW") + # Join multiple list arguments into the space-separated string that we + # want. This ensures that the entirety of the value gets passed as + # compile arguments, which is what the user almost surely intended. + # + # FIXME(#27901): This needs a policy to be implemented. This will be + # available in a future version of CMake. + list(JOIN CMAKE_REQUIRED_FLAGS " " CMAKE_REQUIRED_FLAGS) + else() + #]] + # If CMAKE_REQUIRED_FLAGS contains an unescaped semicolon, anything after + # gets passed as flags to 'cmake' itself. This is probably not intended, + # but we preserve it for compatibility. + set(_CSR_EXTRA_CMAKE_ARGUMENTS "${CMAKE_REQUIRED_FLAGS}") + list(POP_FRONT _CSR_EXTRA_CMAKE_ARGUMENTS CMAKE_REQUIRED_FLAGS) + #[[ + if(NOT CMAKE_REQUIRED_FLAGS STREQUAL "") + cmake_policy(ISSUE_WARNING CMP0999) TODO + endif() + #]] + # There is at least one known instance of users accidentally passing + # '-W' arguments intended for the compiler as arguments to CMake. Since + # CMake complains about unknown '-W' arguments starting with CMake 4.4, + # we need to strip these for compatibility. + string(REPLACE "\\;" "\\\\;" _CSR_EXTRA_CMAKE_ARGUMENTS + "${_CSR_EXTRA_CMAKE_ARGUMENTS}") + list(FILTER _CSR_EXTRA_CMAKE_ARGUMENTS EXCLUDE REGEX "^-W") + #endif() + if(NOT CMAKE_REQUIRED_QUIET) message(CHECK_START "Performing Test ${_var}") endif() @@ -97,7 +131,9 @@ function(CMAKE_CHECK_SOURCE_RUNS _lang _source _var) COMPILE_DEFINITIONS -D${_var} ${CMAKE_REQUIRED_DEFINITIONS} ${CHECK_${_lang}_SOURCE_COMPILES_ADD_LINK_OPTIONS} ${CHECK_${_lang}_SOURCE_COMPILES_ADD_LIBRARIES} - CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} + CMAKE_FLAGS + -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} + ${_CSR_EXTRA_CMAKE_ARGUMENTS} -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH} "${CHECK_${_lang}_SOURCE_COMPILES_ADD_INCLUDES}" "${_CSR_LINK_DIRECTORIES}" diff --git a/Tests/RunCMake/CheckSourceCompiles/Extra.cmake b/Tests/RunCMake/CheckSourceCompiles/Extra.cmake new file mode 100644 index 0000000000..edabced92b --- /dev/null +++ b/Tests/RunCMake/CheckSourceCompiles/Extra.cmake @@ -0,0 +1 @@ +message(WARNING "Hello from Extra.cmake") diff --git a/Tests/RunCMake/CheckSourceCompiles/Issue27893.cmake b/Tests/RunCMake/CheckSourceCompiles/Issue27893.cmake new file mode 100644 index 0000000000..ec3dfe8600 --- /dev/null +++ b/Tests/RunCMake/CheckSourceCompiles/Issue27893.cmake @@ -0,0 +1,9 @@ +enable_language (C) +include(CheckSourceCompiles) + +# If CMAKE_REQUIRED_FLAGS is a list, all items but the first are passed as +# arguments to 'cmake'. This was never supported, but arguments that start with +# '-W' were silently ignored by CMake < 4.4. Starting with 4.4, CMake complains +# about unknown warning flags. Therefore, for compatibility, we strip them. +set(CMAKE_REQUIRED_FLAGS -DFOO -Wseen-by-cmake) +check_source_compiles(C "int main() {return 0;}" SHOULD_BUILD) diff --git a/Tests/RunCMake/CheckSourceCompiles/RequiredFlags-stderr.txt b/Tests/RunCMake/CheckSourceCompiles/RequiredFlags-stderr.txt new file mode 100644 index 0000000000..c7e01537bd --- /dev/null +++ b/Tests/RunCMake/CheckSourceCompiles/RequiredFlags-stderr.txt @@ -0,0 +1,7 @@ +CMake Warning at [^ +]*/Tests/RunCMake/CheckSourceCompiles/Extra\.cmake:[0-9]+ \(message\): + Hello from Extra\.cmake +Call Stack \(most recent call first\): + [^ +]*/Tests/RunCMake/CheckSourceCompiles/RequiredFlags-build/CMakeFiles/CMakeScratch/TryCompile-[^ +]*/CMakeLists\.txt:[0-9]+ \(project\) diff --git a/Tests/RunCMake/CheckSourceCompiles/RequiredFlags.cmake b/Tests/RunCMake/CheckSourceCompiles/RequiredFlags.cmake new file mode 100644 index 0000000000..3d10f5dd4b --- /dev/null +++ b/Tests/RunCMake/CheckSourceCompiles/RequiredFlags.cmake @@ -0,0 +1,17 @@ +enable_language (C) +include(CheckSourceCompiles) + +# If CMAKE_REQUIRED_FLAGS is a list, all items but the first are passed as +# arguments to 'cmake'. For backwards compatibility, test that this works. +# +# It would be nice if we could also test that a literal semicolon makes it +# through. Unfortunately, that needs different levels of escaping depending on +# the generator, because the build rule may or may not be subject to shell +# evaluation. +set(CMAKE_REQUIRED_FLAGS + "-DFUNC=main" + "-DCMAKE_PROJECT_INCLUDE=${CMAKE_CURRENT_LIST_DIR}/Extra.cmake") +check_source_compiles(C "int FUNC() {return 0;}" SHOULD_BUILD) +if(NOT SHOULD_BUILD) + message(SEND_ERROR "Test fail for valid C source.") +endif() diff --git a/Tests/RunCMake/CheckSourceCompiles/RunCMakeTest.cmake b/Tests/RunCMake/CheckSourceCompiles/RunCMakeTest.cmake index afee71e30e..6bfafdf75f 100644 --- a/Tests/RunCMake/CheckSourceCompiles/RunCMakeTest.cmake +++ b/Tests/RunCMake/CheckSourceCompiles/RunCMakeTest.cmake @@ -5,6 +5,9 @@ run_cmake(NonExistentLanguage) run_cmake(UnknownArgument) run_cmake(MultipleLanguages) +run_cmake(RequiredFlags) +run_cmake(Issue27893) + run_cmake(CheckCSourceCompiles) run_cmake(CheckCXXSourceCompiles) run_cmake(CheckSourceCompilesC) diff --git a/Tests/RunCMake/CheckSourceRuns/Extra.cmake b/Tests/RunCMake/CheckSourceRuns/Extra.cmake new file mode 100644 index 0000000000..edabced92b --- /dev/null +++ b/Tests/RunCMake/CheckSourceRuns/Extra.cmake @@ -0,0 +1 @@ +message(WARNING "Hello from Extra.cmake") diff --git a/Tests/RunCMake/CheckSourceRuns/Issue27893.cmake b/Tests/RunCMake/CheckSourceRuns/Issue27893.cmake new file mode 100644 index 0000000000..227d38d216 --- /dev/null +++ b/Tests/RunCMake/CheckSourceRuns/Issue27893.cmake @@ -0,0 +1,9 @@ +enable_language (C) +include(CheckSourceRuns) + +# If CMAKE_REQUIRED_FLAGS is a list, all items but the first are passed as +# arguments to 'cmake'. This was never supported, but arguments that start with +# '-W' were silently ignored by CMake < 4.4. Starting with 4.4, CMake complains +# about unknown warning flags. Therefore, for compatibility, we strip them. +set(CMAKE_REQUIRED_FLAGS -DFOO -Wseen-by-cmake) +check_source_runs(C "int main() {return 0;}" SHOULD_RUN) diff --git a/Tests/RunCMake/CheckSourceRuns/RequiredFlags-stderr.txt b/Tests/RunCMake/CheckSourceRuns/RequiredFlags-stderr.txt new file mode 100644 index 0000000000..341d24407c --- /dev/null +++ b/Tests/RunCMake/CheckSourceRuns/RequiredFlags-stderr.txt @@ -0,0 +1,7 @@ +CMake Warning at [^ +]*/Tests/RunCMake/CheckSourceRuns/Extra\.cmake:[0-9]+ \(message\): + Hello from Extra\.cmake +Call Stack \(most recent call first\): + [^ +]*/Tests/RunCMake/CheckSourceRuns/RequiredFlags-build/CMakeFiles/CMakeScratch/TryCompile-[^ +]*/CMakeLists\.txt:[0-9]+ \(project\) diff --git a/Tests/RunCMake/CheckSourceRuns/RequiredFlags.cmake b/Tests/RunCMake/CheckSourceRuns/RequiredFlags.cmake new file mode 100644 index 0000000000..c54b70de67 --- /dev/null +++ b/Tests/RunCMake/CheckSourceRuns/RequiredFlags.cmake @@ -0,0 +1,17 @@ +enable_language (C) +include(CheckSourceRuns) + +# If CMAKE_REQUIRED_FLAGS is a list, all items but the first are passed as +# arguments to 'cmake'. For backwards compatibility, test that this works. +# +# It would be nice if we could also test that a literal semicolon makes it +# through. Unfortunately, that needs different levels of escaping depending on +# the generator, because the build rule may or may not be subject to shell +# evaluation. +set(CMAKE_REQUIRED_FLAGS + "-DFUNC=main" + "-DCMAKE_PROJECT_INCLUDE=${CMAKE_CURRENT_LIST_DIR}/Extra.cmake") +check_source_runs(C "int FUNC() {return 0;}" SHOULD_RUN) +if(NOT SHOULD_RUN) + message(SEND_ERROR "C check_source_runs failed for valid C executable.") +endif() diff --git a/Tests/RunCMake/CheckSourceRuns/RunCMakeTest.cmake b/Tests/RunCMake/CheckSourceRuns/RunCMakeTest.cmake index 64cecfc0e5..9c51a6be9a 100644 --- a/Tests/RunCMake/CheckSourceRuns/RunCMakeTest.cmake +++ b/Tests/RunCMake/CheckSourceRuns/RunCMakeTest.cmake @@ -4,6 +4,9 @@ run_cmake(NotEnabledLanguage) run_cmake(NonExistentLanguage) run_cmake(UnknownArgument) +run_cmake(RequiredFlags) +run_cmake(Issue27893) + run_cmake(CheckCSourceRuns) run_cmake(CheckCXXSourceRuns) run_cmake(CheckSourceRunsC)