VS: Add COMMON_LANGUAGE_RUNTIME support for "netcore"

Generate `CLRSupport` for Visual Studio projects.

Fixes: #22054
diff --git a/Help/prop_tgt/COMMON_LANGUAGE_RUNTIME.rst b/Help/prop_tgt/COMMON_LANGUAGE_RUNTIME.rst
index 9a01977..7ce0023 100644
--- a/Help/prop_tgt/COMMON_LANGUAGE_RUNTIME.rst
+++ b/Help/prop_tgt/COMMON_LANGUAGE_RUNTIME.rst
@@ -17,6 +17,13 @@
 
   Mixed unmanaged/managed C++ using .NET Framework.
 
+``netcore``
+  .. versionadded:: 3.26
+
+  Mixed unmanaged/managed C++ using .NET Core.
+
+  This required VS 2019's v142 toolset or higher.
+
 ``pure``
 
   Managed C++.
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 321122a..8fd7321 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -8515,9 +8515,14 @@
   //                            lib
   // 2. empty propval:          add /clr as flag, mixed unmanaged/managed
   //                            target, has import lib
-  // 3. any value (safe,pure):  add /clr:[propval] as flag, target with
+  // 3. netcore propval:        add /clr:netcore as flag, mixed
+  //                            unmanaged/managed target, has import lib.
+  // 4. any value (safe,pure):  add /clr:[propval] as flag, target with
   //                            managed code only, no import lib
-  return propval.empty() ? ManagedType::Mixed : ManagedType::Managed;
+  if (propval.empty() || propval == "netcore") {
+    return ManagedType::Mixed;
+  }
+  return ManagedType::Managed;
 }
 
 cmGeneratorTarget::ManagedType cmGeneratorTarget::GetManagedType(
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 4cfb561..c982713 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -3316,9 +3316,12 @@
     }
   }
 
-  if (this->ProjectType != VsProjectType::csproj && clOptions.IsManaged()) {
+  if (this->ProjectType != VsProjectType::csproj &&
+      (clOptions.IsManaged() || clOptions.HasFlag("CLRSupport"))) {
     this->Managed = true;
-    std::string managedType = clOptions.GetFlag("CompileAsManaged");
+    std::string managedType = clOptions.HasFlag("CompileAsManaged")
+      ? clOptions.GetFlag("CompileAsManaged")
+      : "Mixed";
     if (managedType == "Safe" || managedType == "Pure") {
       // force empty calling convention if safe clr is used
       clOptions.AddFlag("CallingConvention", "");
diff --git a/Templates/MSBuild/FlagTables/v142_CL.json b/Templates/MSBuild/FlagTables/v142_CL.json
index c76c040..4f2dce8 100644
--- a/Templates/MSBuild/FlagTables/v142_CL.json
+++ b/Templates/MSBuild/FlagTables/v142_CL.json
@@ -56,6 +56,13 @@
     "flags": []
   },
   {
+    "name": "CLRSupport",
+    "switch": "clr:netcore",
+    "comment": ".NET Core Runtime Support",
+    "value": "NetCore",
+    "flags": []
+  },
+  {
     "name": "WarningLevel",
     "switch": "W0",
     "comment": "Turn Off All Warnings",
diff --git a/Templates/MSBuild/FlagTables/v143_CL.json b/Templates/MSBuild/FlagTables/v143_CL.json
index 993fbf1..119e171 100644
--- a/Templates/MSBuild/FlagTables/v143_CL.json
+++ b/Templates/MSBuild/FlagTables/v143_CL.json
@@ -56,6 +56,13 @@
     "flags": []
   },
   {
+    "name": "CLRSupport",
+    "switch": "clr:netcore",
+    "comment": ".NET Core Runtime Support",
+    "value": "NetCore",
+    "flags": []
+  },
+  {
     "name": "WarningLevel",
     "switch": "W0",
     "comment": "Turn Off All Warnings",
diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
index f027e94..ed74896 100644
--- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
@@ -89,3 +89,10 @@
 run_cmake(VsDotnetTargetFrameworkVersion)
 run_cmake(VsNoCompileBatching)
 run_cmake(DebugInformationFormat)
+run_cmake(VsCLREmpty)
+run_cmake(VsCLRPure)
+run_cmake(VsCLRSafe)
+
+if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.20)
+  run_cmake(VsCLRNetcore)
+endif()
diff --git a/Tests/RunCMake/VS10Project/VsCLREmpty-check.cmake b/Tests/RunCMake/VS10Project/VsCLREmpty-check.cmake
new file mode 100644
index 0000000..990da46
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsCLREmpty-check.cmake
@@ -0,0 +1,24 @@
+set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
+if(NOT EXISTS "${vcProjectFile}")
+  set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
+  return()
+endif()
+
+set(propertyFound FALSE)
+file(STRINGS "${vcProjectFile}" lines)
+foreach(line IN LISTS lines)
+  if(line MATCHES "^ *<CompileAsManaged>(.*)</CompileAsManaged>$")
+    set(propertyFound TRUE)
+    set(expectedValue "true")
+    set(actualValue ${CMAKE_MATCH_1})
+    if(NOT (${actualValue} STREQUAL ${expectedValue}))
+      set(RunCMake_TEST_FAILED "CompileAsManaged \"${actualValue}\" differs from expected value \"${expectedValue}\".")
+      return()
+    endif()
+  endif()
+endforeach()
+
+if(NOT propertyFound)
+  set(RunCMake_TEST_FAILED "Property CompileAsManaged not found in project file.")
+  return()
+endif()
diff --git a/Tests/RunCMake/VS10Project/VsCLREmpty.cmake b/Tests/RunCMake/VS10Project/VsCLREmpty.cmake
new file mode 100644
index 0000000..a622f26
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsCLREmpty.cmake
@@ -0,0 +1,6 @@
+enable_language(CXX)
+
+add_library(foo foo.cpp)
+
+set_target_properties(foo PROPERTIES
+  COMMON_LANGUAGE_RUNTIME "")
diff --git a/Tests/RunCMake/VS10Project/VsCLRNetcore-check.cmake b/Tests/RunCMake/VS10Project/VsCLRNetcore-check.cmake
new file mode 100644
index 0000000..a5058d7
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsCLRNetcore-check.cmake
@@ -0,0 +1,24 @@
+set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
+if(NOT EXISTS "${vcProjectFile}")
+  set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
+  return()
+endif()
+
+set(propertyFound FALSE)
+file(STRINGS "${vcProjectFile}" lines)
+foreach(line IN LISTS lines)
+  if(line MATCHES "^ *<CLRSupport>(.*)</CLRSupport>$")
+    set(propertyFound TRUE)
+    set(expectedValue "NetCore")
+    set(actualValue ${CMAKE_MATCH_1})
+    if(NOT (${actualValue} STREQUAL ${expectedValue}))
+      set(RunCMake_TEST_FAILED "CLRSupport \"${actualValue}\" differs from expected value \"${expectedValue}\".")
+      return()
+    endif()
+  endif()
+endforeach()
+
+if(NOT propertyFound)
+  set(RunCMake_TEST_FAILED "Property CLRSupport not found in project file.")
+  return()
+endif()
diff --git a/Tests/RunCMake/VS10Project/VsCLRNetcore.cmake b/Tests/RunCMake/VS10Project/VsCLRNetcore.cmake
new file mode 100644
index 0000000..c5ec2bc
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsCLRNetcore.cmake
@@ -0,0 +1,6 @@
+enable_language(CXX)
+
+add_library(foo foo.cpp)
+
+set_target_properties(foo PROPERTIES
+  COMMON_LANGUAGE_RUNTIME "netcore")
diff --git a/Tests/RunCMake/VS10Project/VsCLRPure-check.cmake b/Tests/RunCMake/VS10Project/VsCLRPure-check.cmake
new file mode 100644
index 0000000..8ae73eb
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsCLRPure-check.cmake
@@ -0,0 +1,24 @@
+set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
+if(NOT EXISTS "${vcProjectFile}")
+  set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
+  return()
+endif()
+
+set(propertyFound FALSE)
+file(STRINGS "${vcProjectFile}" lines)
+foreach(line IN LISTS lines)
+  if(line MATCHES "^ *<CompileAsManaged>(.*)</CompileAsManaged>$")
+    set(propertyFound TRUE)
+    set(expectedValue "Pure")
+    set(actualValue ${CMAKE_MATCH_1})
+    if(NOT (${actualValue} STREQUAL ${expectedValue}))
+      set(RunCMake_TEST_FAILED "CompileAsManaged \"${actualValue}\" differs from expected value \"${expectedValue}\".")
+      return()
+    endif()
+  endif()
+endforeach()
+
+if(NOT propertyFound)
+  set(RunCMake_TEST_FAILED "Property CompileAsManaged not found in project file.")
+  return()
+endif()
diff --git a/Tests/RunCMake/VS10Project/VsCLRPure.cmake b/Tests/RunCMake/VS10Project/VsCLRPure.cmake
new file mode 100644
index 0000000..f919a1c
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsCLRPure.cmake
@@ -0,0 +1,6 @@
+enable_language(CXX)
+
+add_library(foo foo.cpp)
+
+set_target_properties(foo PROPERTIES
+  COMMON_LANGUAGE_RUNTIME "pure")
diff --git a/Tests/RunCMake/VS10Project/VsCLRSafe-check.cmake b/Tests/RunCMake/VS10Project/VsCLRSafe-check.cmake
new file mode 100644
index 0000000..ebb1f71
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsCLRSafe-check.cmake
@@ -0,0 +1,24 @@
+set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
+if(NOT EXISTS "${vcProjectFile}")
+  set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
+  return()
+endif()
+
+set(propertyFound FALSE)
+file(STRINGS "${vcProjectFile}" lines)
+foreach(line IN LISTS lines)
+  if(line MATCHES "^ *<CompileAsManaged>(.*)</CompileAsManaged>$")
+    set(propertyFound TRUE)
+    set(expectedValue "Safe")
+    set(actualValue ${CMAKE_MATCH_1})
+    if(NOT (${actualValue} STREQUAL ${expectedValue}))
+      set(RunCMake_TEST_FAILED "CompileAsManaged \"${actualValue}\" differs from expected value \"${expectedValue}\".")
+      return()
+    endif()
+  endif()
+endforeach()
+
+if(NOT propertyFound)
+  set(RunCMake_TEST_FAILED "Property CompileAsManaged not found in project file.")
+  return()
+endif()
diff --git a/Tests/RunCMake/VS10Project/VsCLRSafe.cmake b/Tests/RunCMake/VS10Project/VsCLRSafe.cmake
new file mode 100644
index 0000000..5f114bf
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsCLRSafe.cmake
@@ -0,0 +1,6 @@
+enable_language(CXX)
+
+add_library(foo foo.cpp)
+
+set_target_properties(foo PROPERTIES
+  COMMON_LANGUAGE_RUNTIME "safe")