From 14212494bbcb27b6551f45b3e6e8fdd87af015af Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 2 May 2025 14:04:48 -0400 Subject: [PATCH] ASM: Guard exclusion of MSVC C/CXX compiler with a policy Since commit 6baf65ec46 (ASM: Do not consider MSVC C/CXX compiler for generic ASM, 2025-04-08) we no longer mistake `cl` for an assembler. However, some projects unconditionally enable ``ASM``, which worked on Windows only due to that bug. Restore compatibility with such projects by guarding the change behind a new policy ``CMP0194``. Fixes: #26907 Issue: #26617 --- Help/manual/cmake-policies.7.rst | 1 + Help/policy/CMP0194.rst | 27 +++++++++++++++ Help/release/dev/asm-no-msvc.rst | 5 +++ Modules/CMakeASMInformation.cmake | 2 +- Modules/CMakeDetermineASMCompiler.cmake | 34 ++++++++++++++++--- Source/cmPolicies.h | 4 ++- Tests/RunCMake/CMP0194/CMP0194-NEW-result.txt | 1 + Tests/RunCMake/CMP0194/CMP0194-NEW-stderr.txt | 9 +++++ Tests/RunCMake/CMP0194/CMP0194-NEW-stdout.txt | 2 ++ Tests/RunCMake/CMP0194/CMP0194-NEW.cmake | 2 ++ Tests/RunCMake/CMP0194/CMP0194-OLD-stdout.txt | 3 ++ Tests/RunCMake/CMP0194/CMP0194-OLD.cmake | 2 ++ .../RunCMake/CMP0194/CMP0194-WARN-stderr.txt | 10 ++++++ .../RunCMake/CMP0194/CMP0194-WARN-stdout.txt | 3 ++ Tests/RunCMake/CMP0194/CMP0194-WARN.cmake | 2 ++ Tests/RunCMake/CMP0194/CMP0194-common.cmake | 3 ++ Tests/RunCMake/CMP0194/CMakeLists.txt | 3 ++ Tests/RunCMake/CMP0194/RunCMakeTest.cmake | 10 ++++++ Tests/RunCMake/CMakeLists.txt | 4 +++ 19 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 Help/policy/CMP0194.rst create mode 100644 Help/release/dev/asm-no-msvc.rst create mode 100644 Tests/RunCMake/CMP0194/CMP0194-NEW-result.txt create mode 100644 Tests/RunCMake/CMP0194/CMP0194-NEW-stderr.txt create mode 100644 Tests/RunCMake/CMP0194/CMP0194-NEW-stdout.txt create mode 100644 Tests/RunCMake/CMP0194/CMP0194-NEW.cmake create mode 100644 Tests/RunCMake/CMP0194/CMP0194-OLD-stdout.txt create mode 100644 Tests/RunCMake/CMP0194/CMP0194-OLD.cmake create mode 100644 Tests/RunCMake/CMP0194/CMP0194-WARN-stderr.txt create mode 100644 Tests/RunCMake/CMP0194/CMP0194-WARN-stdout.txt create mode 100644 Tests/RunCMake/CMP0194/CMP0194-WARN.cmake create mode 100644 Tests/RunCMake/CMP0194/CMP0194-common.cmake create mode 100644 Tests/RunCMake/CMP0194/CMakeLists.txt create mode 100644 Tests/RunCMake/CMP0194/RunCMakeTest.cmake diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 821adc930f..b9cf81ac13 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -98,6 +98,7 @@ Policies Introduced by CMake 4.1 .. toctree:: :maxdepth: 1 + CMP0194: MSVC is not an assembler for language ASM. CMP0193: GNUInstallDirs caches CMAKE_INSTALL_* with leading 'usr/' for install prefix '/'. CMP0192: GNUInstallDirs uses absolute SYSCONFDIR, LOCALSTATEDIR, and RUNSTATEDIR in special prefixes. CMP0191: The FindCABLE module is removed. diff --git a/Help/policy/CMP0194.rst b/Help/policy/CMP0194.rst new file mode 100644 index 0000000000..8b47438da5 --- /dev/null +++ b/Help/policy/CMP0194.rst @@ -0,0 +1,27 @@ +CMP0194 +------- + +.. versionadded:: 4.1 + +MSVC is not an assembler for language ASM. + +When enabling the ``ASM`` language, CMake considers C compiler drivers +as assembler candidates. CMake 4.0 and below accidentally selected +MSVC's ``cl`` compiler as the ``CMAKE_ASM_COMPILER``, allowing the ``ASM`` +language to be enabled on Windows even though ``cl`` does not support +assembler sources. CMake 4.1 and above prefer to reject ``cl`` as an +assembler candidate, but some existing projects unconditionally enable +``ASM`` on Windows even though they add no assembler sources. This +policy provides compatibility for such projects to allow them to +configure as before. + +The ``OLD`` behavior for this policy is to successfully enable ``ASM`` +even if ``cl`` is the only available candidate. The ``NEW`` behavior +for this policy is to not consider ``cl`` as a candidate assembler +for the ``ASM`` language. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.1 +.. |WARNS_OR_DOES_NOT_WARN| replace:: warns +.. include:: include/STANDARD_ADVICE.rst + +.. include:: include/DEPRECATED.rst diff --git a/Help/release/dev/asm-no-msvc.rst b/Help/release/dev/asm-no-msvc.rst new file mode 100644 index 0000000000..623493ad76 --- /dev/null +++ b/Help/release/dev/asm-no-msvc.rst @@ -0,0 +1,5 @@ +asm-no-msvc +----------- + +* Enabling ``ASM`` no longer accidentally succeeds using ``MSVC``'s ``cl`` + C compiler as an assembler. See policy :policy:`CMP0194`. diff --git a/Modules/CMakeASMInformation.cmake b/Modules/CMakeASMInformation.cmake index 7530070c9b..b56ece1e13 100644 --- a/Modules/CMakeASMInformation.cmake +++ b/Modules/CMakeASMInformation.cmake @@ -22,7 +22,7 @@ if(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID) include(Compiler/${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}-ASM${ASM_DIALECT} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) endif() if(NOT _INCLUDED_FILE) - if("ASM${ASM_DIALECT}" STREQUAL "ASM") + if("ASM${ASM_DIALECT}" STREQUAL "ASM" AND CMAKE_ASM${ASM_DIALECT}_COMPILER_ID) message(STATUS "Warning: Did not find file Compiler/${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}-ASM${ASM_DIALECT}") endif() include(Platform/${CMAKE_BASE_NAME} OPTIONAL) diff --git a/Modules/CMakeDetermineASMCompiler.cmake b/Modules/CMakeDetermineASMCompiler.cmake index 2f8077d3b6..7e0dffad35 100644 --- a/Modules/CMakeDetermineASMCompiler.cmake +++ b/Modules/CMakeDetermineASMCompiler.cmake @@ -6,6 +6,8 @@ include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake) +cmake_policy(GET CMP0194 _CMAKE_ASM_CMP0194) + if(NOT CMAKE_ASM${ASM_DIALECT}_COMPILER) # prefer the environment variable ASM if(NOT $ENV{ASM${ASM_DIALECT}} STREQUAL "") @@ -21,19 +23,32 @@ if(NOT CMAKE_ASM${ASM_DIALECT}_COMPILER) # finally list compilers to try if("ASM${ASM_DIALECT}" STREQUAL "ASM") # the generic assembler support if(NOT CMAKE_ASM_COMPILER_INIT) - if(CMAKE_C_COMPILER_LOADED AND NOT CMAKE_C_COMPILER_ID MATCHES "^(MSVC)$") + if(_CMAKE_ASM_CMP0194 STREQUAL "NEW") + set(_CMAKE_ASM_REGEX_MSVC "^(MSVC)$") + set(_CMAKE_ASM_REGEX_CL "(^|/)[Cc][Ll](\\.|$)") + set(_CMAKE_ASM_MAYBE_CL "") + else() + set(_CMAKE_ASM_REGEX_MSVC "CMP0194_OLD_MSVC_NOT_EXCLUDED") + set(_CMAKE_ASM_REGEX_CL "CMP0194_OLD_MSVC_NOT_EXCLUDED") + set(_CMAKE_ASM_MAYBE_CL "cl") + endif() + if(CMAKE_C_COMPILER_LOADED AND NOT CMAKE_C_COMPILER_ID MATCHES "${_CMAKE_ASM_REGEX_MSVC}") set(CMAKE_ASM_COMPILER_LIST ${CMAKE_C_COMPILER}) - elseif(NOT CMAKE_C_COMPILER_LOADED AND CMAKE_C_COMPILER AND NOT CMAKE_C_COMPILER MATCHES "(^|/)[Cc][Ll](\\.|$)") + elseif(NOT CMAKE_C_COMPILER_LOADED AND CMAKE_C_COMPILER AND NOT CMAKE_C_COMPILER MATCHES "${_CMAKE_ASM_REGEX_CL}") set(CMAKE_ASM_COMPILER_LIST ${CMAKE_C_COMPILER}) - elseif(CMAKE_CXX_COMPILER_LOADED AND NOT CMAKE_CXX_COMPILER_ID MATCHES "^(MSVC)$") + elseif(CMAKE_CXX_COMPILER_LOADED AND NOT CMAKE_CXX_COMPILER_ID MATCHES "${_CMAKE_ASM_REGEX_MSVC}") set(CMAKE_ASM_COMPILER_LIST ${CMAKE_CXX_COMPILER}) - elseif(NOT CMAKE_CXX_COMPILER_LOADED AND CMAKE_CXX_COMPILER AND NOT CMAKE_CXX_COMPILER MATCHES "(^|/)[Cc][Ll](\\.|$)") + elseif(NOT CMAKE_CXX_COMPILER_LOADED AND CMAKE_CXX_COMPILER AND NOT CMAKE_CXX_COMPILER MATCHES "${_CMAKE_ASM_REGEX_CL}") set(CMAKE_ASM_COMPILER_LIST ${CMAKE_CXX_COMPILER}) else() # List all default C and CXX compilers set(CMAKE_ASM_COMPILER_LIST ${_CMAKE_TOOLCHAIN_PREFIX}cc ${_CMAKE_TOOLCHAIN_PREFIX}gcc xlc + ${_CMAKE_ASM_MAYBE_CL} CC ${_CMAKE_TOOLCHAIN_PREFIX}c++ ${_CMAKE_TOOLCHAIN_PREFIX}g++ xlC) + unset(_CMAKE_ASM_MAYBE_CL) + unset(_CMAKE_ASM_REGEX_CL) + unset(_CMAKE_ASM_REGEX_MSVC) endif() endif() else() # some specific assembler "dialect" @@ -100,7 +115,11 @@ if(NOT CMAKE_ASM${ASM_DIALECT}_COMPILER_ID) list(APPEND CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDORS MSVC ) set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_MSVC "-?") - set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_MSVC "Microsoft.*Macro Assembler") + if(_CMAKE_ASM_CMP0194 STREQUAL "NEW") + set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_MSVC "Microsoft.*Macro Assembler") + else() + set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_MSVC "Microsoft") + endif() list(APPEND CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDORS TI ) set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_TI "-h") @@ -200,6 +219,11 @@ else() message(STATUS "The ASM${ASM_DIALECT} compiler identification is unknown") endif() +if("ASM${ASM_DIALECT}" STREQUAL "ASM" AND CMAKE_ASM_COMPILER_ID STREQUAL "MSVC" AND _CMAKE_ASM_CMP0194 STREQUAL "") + cmake_policy(GET_WARNING CMP0194 _CMAKE_ASM_CMP0194_WARNING) + message(AUTHOR_WARNING "${_CMAKE_ASM_CMP0194_WARNING}") +endif() + # If we have a gas/as cross compiler, they have usually some prefix, like # e.g. powerpc-linux-gas, arm-elf-gas or i586-mingw32msvc-gas , optionally # with a 3-component version number at the end diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 0d65c49f89..9b17f42416 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -578,7 +578,9 @@ class cmMakefile; SELECT(POLICY, CMP0193, \ "GNUInstallDirs caches CMAKE_INSTALL_* with leading 'usr/' for " \ "install prefix '/'.", \ - 4, 1, 0, WARN) + 4, 1, 0, WARN) \ + SELECT(POLICY, CMP194, "MSVC is not an assembler for language ASM.", 4, 1, \ + 0, WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ diff --git a/Tests/RunCMake/CMP0194/CMP0194-NEW-result.txt b/Tests/RunCMake/CMP0194/CMP0194-NEW-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/CMP0194/CMP0194-NEW-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMP0194/CMP0194-NEW-stderr.txt b/Tests/RunCMake/CMP0194/CMP0194-NEW-stderr.txt new file mode 100644 index 0000000000..4e755967af --- /dev/null +++ b/Tests/RunCMake/CMP0194/CMP0194-NEW-stderr.txt @@ -0,0 +1,9 @@ +^CMake Error at CMP0194-common\.cmake:[0-9]+ \(enable_language\): + No CMAKE_ASM_COMPILER could be found\. +( + Tell CMake where to find the compiler by setting either the environment + variable "ASM" or the CMake cache entry CMAKE_ASM_COMPILER to the full path + to the compiler, or to the compiler name if it is in the PATH\.)? +Call Stack \(most recent call first\): + CMP0194-NEW\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/CMP0194/CMP0194-NEW-stdout.txt b/Tests/RunCMake/CMP0194/CMP0194-NEW-stdout.txt new file mode 100644 index 0000000000..074c81e399 --- /dev/null +++ b/Tests/RunCMake/CMP0194/CMP0194-NEW-stdout.txt @@ -0,0 +1,2 @@ +-- The ASM compiler identification is unknown +-- Didn't find assembler diff --git a/Tests/RunCMake/CMP0194/CMP0194-NEW.cmake b/Tests/RunCMake/CMP0194/CMP0194-NEW.cmake new file mode 100644 index 0000000000..f8c3763a86 --- /dev/null +++ b/Tests/RunCMake/CMP0194/CMP0194-NEW.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0194 NEW) +include(CMP0194-common.cmake) diff --git a/Tests/RunCMake/CMP0194/CMP0194-OLD-stdout.txt b/Tests/RunCMake/CMP0194/CMP0194-OLD-stdout.txt new file mode 100644 index 0000000000..66b63baef4 --- /dev/null +++ b/Tests/RunCMake/CMP0194/CMP0194-OLD-stdout.txt @@ -0,0 +1,3 @@ +-- The ASM compiler identification is MSVC +-- Found assembler: [^ +]*/cl\.exe diff --git a/Tests/RunCMake/CMP0194/CMP0194-OLD.cmake b/Tests/RunCMake/CMP0194/CMP0194-OLD.cmake new file mode 100644 index 0000000000..fa0dffa9f7 --- /dev/null +++ b/Tests/RunCMake/CMP0194/CMP0194-OLD.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0194 OLD) +include(CMP0194-common.cmake) diff --git a/Tests/RunCMake/CMP0194/CMP0194-WARN-stderr.txt b/Tests/RunCMake/CMP0194/CMP0194-WARN-stderr.txt new file mode 100644 index 0000000000..4e5d5cbbd8 --- /dev/null +++ b/Tests/RunCMake/CMP0194/CMP0194-WARN-stderr.txt @@ -0,0 +1,10 @@ +^CMake Warning \(dev\) at [^ +]*/Modules/CMakeDetermineASMCompiler.cmake:[0-9]+ \(message\): + Policy CMP194 is not set: MSVC is not an assembler for language ASM\. Run + "cmake --help-policy CMP194" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0194-common\.cmake:[0-9]+ \(enable_language\) + CMP0194-WARN\.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\.$ diff --git a/Tests/RunCMake/CMP0194/CMP0194-WARN-stdout.txt b/Tests/RunCMake/CMP0194/CMP0194-WARN-stdout.txt new file mode 100644 index 0000000000..66b63baef4 --- /dev/null +++ b/Tests/RunCMake/CMP0194/CMP0194-WARN-stdout.txt @@ -0,0 +1,3 @@ +-- The ASM compiler identification is MSVC +-- Found assembler: [^ +]*/cl\.exe diff --git a/Tests/RunCMake/CMP0194/CMP0194-WARN.cmake b/Tests/RunCMake/CMP0194/CMP0194-WARN.cmake new file mode 100644 index 0000000000..4049bab6ee --- /dev/null +++ b/Tests/RunCMake/CMP0194/CMP0194-WARN.cmake @@ -0,0 +1,2 @@ +# CMP0194 is unset +include(CMP0194-common.cmake) diff --git a/Tests/RunCMake/CMP0194/CMP0194-common.cmake b/Tests/RunCMake/CMP0194/CMP0194-common.cmake new file mode 100644 index 0000000000..47aa914c22 --- /dev/null +++ b/Tests/RunCMake/CMP0194/CMP0194-common.cmake @@ -0,0 +1,3 @@ +enable_language(C) +set(ENV{PATH} "") +enable_language(ASM) diff --git a/Tests/RunCMake/CMP0194/CMakeLists.txt b/Tests/RunCMake/CMP0194/CMakeLists.txt new file mode 100644 index 0000000000..955802cde9 --- /dev/null +++ b/Tests/RunCMake/CMP0194/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.0) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMP0194/RunCMakeTest.cmake b/Tests/RunCMake/CMP0194/RunCMakeTest.cmake new file mode 100644 index 0000000000..ea1b475c4f --- /dev/null +++ b/Tests/RunCMake/CMP0194/RunCMakeTest.cmake @@ -0,0 +1,10 @@ +include(RunCMake) + +# The test cases empty the PATH before enabling ASM to avoid finding +# another assembler in the caller's environment. However, old +# versions of MSVC do not support running `cl` without the PATH set. +if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 16) + run_cmake(CMP0194-WARN) + run_cmake(CMP0194-OLD) +endif() +run_cmake(CMP0194-NEW) diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 243dce459f..7f42b29e51 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -181,6 +181,10 @@ add_RunCMake_test(CMP0171) add_RunCMake_test(CMP0173) add_RunCMake_test(CMP0187) +if(CMAKE_C_COMPILER_ID STREQUAL "MSVC") + add_RunCMake_test(CMP0194 -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}) +endif() + # The test for Policy 65 requires the use of the # CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode # generators ignore. The policy will have no effect on those generators.