MSVC: Move link -machine flag out of CMAKE_*_LINKER_FLAGS

The `CMAKE_{EXE,SHARED,MODULE,STATIC}_LINKER_FLAGS` variables are
not language-specific, so multiple languages' toolchains may disagree
about if/how to pass the flag through a compiler driver to the linker.
Furthermore, carrying the flag in public-facing variables allows projects
or users to change it even though it is required.  Add policy CMP0197
to remove the flag from the public-facing variables and generate it
automatically instead:

* For command-line generators, add the `-machine:` flag to the
  linker and archiver rule variables.

* For Visual Studio generators, we do not need to explicitly add the
  link `-machine:` flag.  MSBuild automatically adds it, and the new
  behavior actually removes a duplicate we generated previously.

Issue: #21934
This commit is contained in:
Brad King
2025-05-30 18:47:31 -04:00
parent bb8baacbcc
commit 92d6126450
18 changed files with 131 additions and 7 deletions

View File

@@ -98,6 +98,7 @@ Policies Introduced by CMake 4.1
.. toctree::
:maxdepth: 1
CMP0197: MSVC link -machine: flag is not in CMAKE_*_LINKER_FLAGS. </policy/CMP0197>
CMP0196: The CMakeDetermineVSServicePack module is removed. </policy/CMP0196>
CMP0195: Swift modules in build trees use the Swift module directory structure. </policy/CMP0195>
CMP0194: MSVC is not an assembler for language ASM. </policy/CMP0194>

45
Help/policy/CMP0197.rst Normal file
View File

@@ -0,0 +1,45 @@
CMP0197
-------
.. versionadded:: 4.1
MSVC link ``-machine:`` flag is not in ``CMAKE_*_LINKER_FLAGS``.
When using MSVC-like compilers in CMake 4.0 and below, the linker's
``-machine:`` flag is added via language-agnostic linker flag variables:
* :variable:`CMAKE_EXE_LINKER_FLAGS`
* :variable:`CMAKE_SHARED_LINKER_FLAGS`
* :variable:`CMAKE_MODULE_LINKER_FLAGS`
* :variable:`CMAKE_STATIC_LINKER_FLAGS`
This is problematic when multiple languages' toolchains disagree about
if/how to pass the flag through a compiler driver to the linker.
Furthermore, carrying the flag in public-facing variables allows projects
or users to change it even though it is required.
CMake 4.1 and above prefer to leave the ``-machine:`` flag out of
``CMAKE_*_LINKER_FLAGS`` variables, and instead generate the link
flag automatically where needed.
This policy provides compatibility with projects that have not been updated
to expect the lack of the ``-machine:`` flags. The policy setting takes
effect as of the first :command:`project` or :command:`enable_language`
command that initializes the above-listed ``CMAKE_*_LINKER_FLAGS`` variables.
.. note::
Once the policy has taken effect at the top of a project for a given
language, that choice must be used throughout the tree for that language.
In projects that have nested projects in subdirectories, be sure to
convert everything together.
The ``OLD`` behavior for this policy is to place the MSVC ``-machine:``
flag in ``CMAKE_*_LINKER_FLAGS``. The ``NEW`` behavior for this policy
is to *not* place the MSVC ``-machine:`` flag in ``CMAKE_*_LINKER_FLAGS``.
.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.1
.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
.. include:: include/STANDARD_ADVICE.rst
.. include:: include/DEPRECATED.rst

View File

@@ -0,0 +1,5 @@
msvc-link-machine
-----------------
* The MSVC link ``-machine:`` flag is no longer added to the
``CMAKE_*_LINKER_FLAGS`` variables. See policy :policy:`CMP0197`.

View File

@@ -47,7 +47,7 @@ macro(__windows_compiler_intel lang)
if (NOT "${lang}" STREQUAL "Fortran" OR CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 2022.1)
# The Fortran driver does not support -fuse-ld=llvm-lib before compiler version 2022.1
set(CMAKE_${lang}_CREATE_STATIC_LIBRARY
"<CMAKE_${lang}_COMPILER> ${CMAKE_CL_NOLOGO} <CMAKE_${lang}_LINK_FLAGS> <OBJECTS> ${CMAKE_START_TEMP_FILE} -fuse-ld=llvm-lib -o <TARGET> <LINK_FLAGS> <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}")
"<CMAKE_${lang}_COMPILER> ${CMAKE_CL_NOLOGO}${_PLATFORM_ARCHIVE_FLAGS} <CMAKE_${lang}_LINK_FLAGS> <OBJECTS> ${CMAKE_START_TEMP_FILE} -fuse-ld=llvm-lib -o <TARGET> <LINK_FLAGS> <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}")
endif()
set(CMAKE_DEPFILE_FLAGS_${lang} "-QMD -QMT <DEP_TARGET> -QMF <DEP_FILE>")

View File

@@ -276,7 +276,12 @@ unset(_GR)
set(CMAKE_CXX_STANDARD_LIBRARIES_INIT "${CMAKE_C_STANDARD_LIBRARIES_INIT}")
set(_machine "/machine")
cmake_policy(GET CMP0197 CMAKE_MSVC_CMP0197)
if(CMAKE_MSVC_CMP0197 STREQUAL "NEW")
set(_machine "-machine")
else()
set(_machine "/machine")
endif()
# executable linker flags
set (CMAKE_LINK_DEF_FILE_FLAG "/DEF:")
@@ -321,7 +326,9 @@ if(NOT WINDOWS_PHONE AND NOT WINDOWS_STORE AND NOT CMAKE_SYSTEM_NAME STREQUAL "W
endif()
foreach(t EXE SHARED MODULE)
string(APPEND CMAKE_${t}_LINKER_FLAGS_INIT " ${_MACHINE_ARCH_FLAG}")
if(NOT CMAKE_MSVC_CMP0197 STREQUAL "NEW")
string(APPEND CMAKE_${t}_LINKER_FLAGS_INIT " ${_MACHINE_ARCH_FLAG}")
endif()
if (CMAKE_COMPILER_SUPPORTS_PDBTYPE)
string(APPEND CMAKE_${t}_LINKER_FLAGS_DEBUG_INIT " ${_Wl}/debug ${_Wl}/pdbtype:sept ${MSVC_INCREMENTAL_YES_FLAG}")
string(APPEND CMAKE_${t}_LINKER_FLAGS_RELWITHDEBINFO_INIT " ${_Wl}/debug ${_Wl}/pdbtype:sept ${MSVC_INCREMENTAL_YES_FLAG}")
@@ -340,11 +347,19 @@ if(CMAKE_SYSTEM_NAME STREQUAL "WindowsCE")
elseif(CMAKE_SYSTEM_NAME STREQUAL "WindowsKernelModeDriver")
string(APPEND _PLATFORM_LINK_FLAGS " -subsystem:native -kernel -MANIFEST:NO")
endif()
if(CMAKE_MSVC_CMP0197 STREQUAL "NEW")
string(APPEND _PLATFORM_LINK_FLAGS " ${_MACHINE_ARCH_FLAG}")
endif()
set(_PLATFORM_ARCHIVE_FLAGS "")
if((_MSVC_C_ARCHITECTURE_FAMILY STREQUAL "ARM64EC") OR (_MSVC_CXX_ARCHITECTURE_FAMILY STREQUAL "ARM64EC"))
set(_MACHINE_ARCH_FLAG " ${_Wl}${_machine}:ARM64X")
endif()
string(APPEND CMAKE_STATIC_LINKER_FLAGS_INIT " ${_MACHINE_ARCH_FLAG}")
if(CMAKE_MSVC_CMP0197 STREQUAL "NEW")
string(APPEND _PLATFORM_ARCHIVE_FLAGS " ${_MACHINE_ARCH_FLAG}")
else()
string(APPEND CMAKE_STATIC_LINKER_FLAGS_INIT " ${_MACHINE_ARCH_FLAG}")
endif()
unset(_MACHINE_ARCH_FLAG)
unset(_machine)
@@ -388,7 +403,7 @@ macro(__windows_compiler_msvc lang)
unset(_DLL_DRIVER)
set(CMAKE_${lang}_CREATE_SHARED_MODULE ${CMAKE_${lang}_CREATE_SHARED_LIBRARY})
set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "<CMAKE_AR> ${CMAKE_CL_NOLOGO} <LINK_FLAGS> /out:<TARGET> <OBJECTS> ")
set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "<CMAKE_AR> ${CMAKE_CL_NOLOGO}${_PLATFORM_ARCHIVE_FLAGS} <LINK_FLAGS> /out:<TARGET> <OBJECTS> ")
set(CMAKE_${lang}_COMPILE_OBJECT
"<CMAKE_${lang}_COMPILER> ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO}${_COMPILE_${lang}} <DEFINES> <INCLUDES> <FLAGS> /Fo<OBJECT> /Fd<TARGET_COMPILE_PDB>${_FS_${lang}} -c <SOURCE>${CMAKE_END_TEMP_FILE}")

View File

@@ -17,7 +17,7 @@ set(CMAKE_CUDA_CREATE_SHARED_LIBRARY
"${_CMAKE_VS_LINK_DLL}<CMAKE_LINKER> ${CMAKE_CL_NOLOGO} <OBJECTS> ${CMAKE_START_TEMP_FILE} /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /dll /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PLATFORM_LINK_FLAGS} <LINK_FLAGS> <LINK_LIBRARIES>${__IMPLICIT_LINKS} ${CMAKE_END_TEMP_FILE}")
set(CMAKE_CUDA_CREATE_SHARED_MODULE ${CMAKE_CUDA_CREATE_SHARED_LIBRARY})
set(CMAKE_CUDA_CREATE_STATIC_LIBRARY "<CMAKE_AR> ${CMAKE_CL_NOLOGO} <LINK_FLAGS> /out:<TARGET> <OBJECTS> ")
set(CMAKE_CUDA_CREATE_STATIC_LIBRARY "<CMAKE_AR> ${CMAKE_CL_NOLOGO}${_PLATFORM_ARCHIVE_FLAGS} <LINK_FLAGS> /out:<TARGET> <OBJECTS> ")
set(CMAKE_CUDA_LINKER_SUPPORTS_PDB ON)
set(CMAKE_CUDA_LINK_EXECUTABLE
"${_CMAKE_VS_LINK_EXE}<CMAKE_LINKER> ${CMAKE_CL_NOLOGO} <OBJECTS> ${CMAKE_START_TEMP_FILE} /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PLATFORM_LINK_FLAGS} <LINK_FLAGS> <LINK_LIBRARIES>${__IMPLICIT_LINKS} ${CMAKE_END_TEMP_FILE}")

View File

@@ -91,6 +91,7 @@ std::string const kCMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT =
"CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT";
std::string const kCMAKE_MSVC_RUNTIME_CHECKS_DEFAULT =
"CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT";
std::string const kCMAKE_MSVC_CMP0197 = "CMAKE_MSVC_CMP0197";
/* GHS Multi platform variables */
std::set<std::string> const ghs_platform_vars{
@@ -684,6 +685,12 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode(
fprintf(fout, "cmake_policy(SET CMP0128 OLD)\n");
}
/* Set MSVC link -machine: policy to match outer project. */
if (cmValue cmp0197 = this->Makefile->GetDefinition(kCMAKE_MSVC_CMP0197)) {
fprintf(fout, "cmake_policy(SET CMP0197 %s)\n",
*cmp0197 == "NEW"_s ? "NEW" : "OLD");
}
std::string projectLangs;
for (std::string const& li : testLangs) {
projectLangs += cmStrCat(' ', li);

View File

@@ -586,7 +586,10 @@ class cmMakefile;
"Swift modules in build trees use the Swift module directory structure.", \
4, 1, 0, WARN) \
SELECT(POLICY, CMP0196, \
"The CMakeDetermineVSServicePack module is removed.", 4, 1, 0, WARN)
"The CMakeDetermineVSServicePack module is removed.", 4, 1, 0, WARN) \
SELECT(POLICY, CMP0197, \
"MSVC link -machine: flag is not in CMAKE_*_LINKER_FLAGS.", 4, 1, 0, \
WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \

View File

@@ -0,0 +1,2 @@
[ ,][/-][Mm][Aa][Cc][Hh][Ii][Nn][Ee]:[A-Za-z0-9_]+[ ]|[Vv][Cc](98|7)\\\\[Bb][Ii][Nn]\\\\[Ll][Ii][Nn][Kk]\.[Ee][Xx][Ee] [^
]*@[^ ]+\\\\nm

View File

@@ -0,0 +1,2 @@
cmake_policy(SET CMP0197 NEW)
include(CMP0197-common.cmake)

View File

@@ -0,0 +1 @@
[" ,]/[Mm][Aa][Cc][Hh][Ii][Nn][Ee]:[A-Za-z0-9_]+[ "]

View File

@@ -0,0 +1,2 @@
cmake_policy(SET CMP0197 OLD)
include(CMP0197-common.cmake)

View File

@@ -0,0 +1 @@
[" ,]/[Mm][Aa][Cc][Hh][Ii][Nn][Ee]:[A-Za-z0-9_]+[ "]

View File

@@ -0,0 +1,2 @@
include(CMP0197-common.cmake)

View File

@@ -0,0 +1 @@
[ ,][/-][Mm][Aa][Cc][Hh][Ii][Nn][Ee]:[A-Za-z0-9_]+[ ]

View File

@@ -0,0 +1,20 @@
enable_language(C)
# Make sure the compile command is not hidden.
string(REPLACE "${CMAKE_START_TEMP_FILE}" "" CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE}")
string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE}")
cmake_policy(GET CMP0197 cmp0197)
foreach(t EXE SHARED MODULE STATIC)
if(cmp0197 STREQUAL "NEW")
if("${CMAKE_${t}_LINKER_FLAGS}" MATCHES "([/-][Mm][Aa][Cc][Hh][Ii][Nn][Ee]:)")
message(SEND_ERROR "CMAKE_${t}_LINKER_FLAGS has '${CMAKE_MATCH_1}' under NEW behavior")
endif()
else()
if(NOT " ${CMAKE_${t}_LINKER_FLAGS} " MATCHES "[ ,]/machine:[A-Za-z0-9_]+ ")
message(SEND_ERROR "CMAKE_${t}_LINKER_FLAGS does not have '/machine:' under OLD behavior")
endif()
endif()
endforeach()
add_executable(main main.c)

View File

@@ -3,3 +3,16 @@ include(RunCMake)
run_cmake(CMP0092-WARN)
run_cmake(CMP0092-OLD)
run_cmake(CMP0092-NEW)
function(run_CMP0197 pol)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0197-${pol})
run_cmake(CMP0197-${pol})
set(RunCMake_TEST_NO_CLEAN 1)
set(RunCMake_TEST_OUTPUT_MERGE 1)
set(RunCMake-stdout-file "CMP0197-build-stdout.txt")
run_cmake_command(CMP0197-${pol}-build ${CMAKE_COMMAND} --build . --config Debug --verbose)
endfunction()
run_CMP0197(WARN)
run_CMP0197(OLD)
run_CMP0197(NEW)

View File

@@ -0,0 +1,4 @@
int main(void)
{
return 0;
}