Merge topic 'ninja-multi-cross-configs'

b7a2baf38c Ninja Multi-Config: Add variable to control configs used in cross-config build

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !4269
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 393735e..3a62371 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -185,6 +185,7 @@
    /prop_tgt/DEPLOYMENT_ADDITIONAL_FILES
    /prop_tgt/DEPRECATION
    /prop_tgt/DISABLE_PRECOMPILE_HEADERS
+   /prop_tgt/DOTNET_TARGET_FRAMEWORK
    /prop_tgt/DOTNET_TARGET_FRAMEWORK_VERSION
    /prop_tgt/EchoString
    /prop_tgt/ENABLE_EXPORTS
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 10d7f2c..1f5b39c 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -48,6 +48,7 @@
    /variable/CMAKE_DEBUG_TARGET_PROPERTIES
    /variable/CMAKE_DIRECTORY_LABELS
    /variable/CMAKE_DL_LIBS
+   /variable/CMAKE_DOTNET_TARGET_FRAMEWORK
    /variable/CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION
    /variable/CMAKE_EDIT_COMMAND
    /variable/CMAKE_EXECUTABLE_SUFFIX
diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst
index 4315f0a..44b1f2f 100644
--- a/Help/manual/cmake.1.rst
+++ b/Help/manual/cmake.1.rst
@@ -279,7 +279,9 @@
          "file": "/full/path/to/the/CMake/file.txt",
          "line": 0,
          "cmd": "add_executable",
-         "args": ["foo", "bar"]
+         "args": ["foo", "bar"],
+         "time": 1579512535.9687231,
+         "frame": 2
        }
 
      The members are:
@@ -288,8 +290,8 @@
        The full path to the CMake source file where the function
        was called.
 
-      ``line``
-       The line in `file` of the function call.
+     ``line``
+       The line in ``file`` of the function call.
 
      ``cmd``
        The name of the function that was called.
@@ -297,6 +299,12 @@
      ``args``
        A string list of all function parameters.
 
+     ``time``
+       Timestamp (seconds since epoch) of the function call.
+
+     ``frame``
+       Stack frame depth of the function that was called.
+
      Additionally, the first JSON document outputted contains the
      ``version`` key for the current major and minor version of the
 
diff --git a/Help/prop_tgt/DOTNET_TARGET_FRAMEWORK.rst b/Help/prop_tgt/DOTNET_TARGET_FRAMEWORK.rst
new file mode 100644
index 0000000..8698eb6
--- /dev/null
+++ b/Help/prop_tgt/DOTNET_TARGET_FRAMEWORK.rst
@@ -0,0 +1,13 @@
+DOTNET_TARGET_FRAMEWORK
+-----------------------
+
+Specify the .NET target framework.
+
+Used to specify the .NET target framework for C++/CLI and C#.  For
+example: ``netcoreapp2.1``.
+
+This property is only evaluated for :ref:`Visual Studio Generators`
+VS 2010 and above.
+
+Can be initialized for all targets using the variable
+:variable:`CMAKE_DOTNET_TARGET_FRAMEWORK`.
diff --git a/Help/prop_tgt/DOTNET_TARGET_FRAMEWORK_VERSION.rst b/Help/prop_tgt/DOTNET_TARGET_FRAMEWORK_VERSION.rst
index c100326..b33f4fb 100644
--- a/Help/prop_tgt/DOTNET_TARGET_FRAMEWORK_VERSION.rst
+++ b/Help/prop_tgt/DOTNET_TARGET_FRAMEWORK_VERSION.rst
@@ -3,11 +3,13 @@
 
 Specify the .NET target framework version.
 
-Used to specify the .NET target framework version for C++/CLI.  For
-example: ``v4.5``.
+Used to specify the .NET target framework version for C++/CLI and C#.
+For example: ``v4.5``.
 
 This property is only evaluated for :ref:`Visual Studio Generators`
 VS 2010 and above.
 
-Can be initialized for all targets using the variable
-:variable:`CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION`.
+To initialize this variable for all targets set
+:variable:`CMAKE_DOTNET_TARGET_FRAMEWORK` or
+:variable:`CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION`. If both are set,
+the latter is ignored.
diff --git a/Help/prop_tgt/VS_DOTNET_TARGET_FRAMEWORK_VERSION.rst b/Help/prop_tgt/VS_DOTNET_TARGET_FRAMEWORK_VERSION.rst
index 9f5a313..6cb8f86 100644
--- a/Help/prop_tgt/VS_DOTNET_TARGET_FRAMEWORK_VERSION.rst
+++ b/Help/prop_tgt/VS_DOTNET_TARGET_FRAMEWORK_VERSION.rst
@@ -3,8 +3,9 @@
 
 Specify the .NET target framework version.
 
-Used to specify the .NET target framework version for C++/CLI.  For
+Used to specify the .NET target framework version for C++/CLI. For
 example, "v4.5".
 
 This property is deprecated and should not be used anymore. Use
+:prop_tgt:`DOTNET_TARGET_FRAMEWORK` or
 :prop_tgt:`DOTNET_TARGET_FRAMEWORK_VERSION` instead.
diff --git a/Help/release/dev/vs-dotnet-standard-core.rst b/Help/release/dev/vs-dotnet-standard-core.rst
new file mode 100644
index 0000000..9bb292e
--- /dev/null
+++ b/Help/release/dev/vs-dotnet-standard-core.rst
@@ -0,0 +1,7 @@
+vs-dotnet-standard-core
+-----------------------
+
+* :ref:`Visual Studio Generators` for VS 2010 and above learned to
+  support .NET Standard and .NET Core.  See the
+  :prop_tgt:`DOTNET_TARGET_FRAMEWORK` target property and
+  associated :variable:`CMAKE_DOTNET_TARGET_FRAMEWORK` variable.
diff --git a/Help/variable/CMAKE_DOTNET_TARGET_FRAMEWORK.rst b/Help/variable/CMAKE_DOTNET_TARGET_FRAMEWORK.rst
new file mode 100644
index 0000000..8edcd1e
--- /dev/null
+++ b/Help/variable/CMAKE_DOTNET_TARGET_FRAMEWORK.rst
@@ -0,0 +1,16 @@
+CMAKE_DOTNET_TARGET_FRAMEWORK
+-----------------------------
+
+Default value for :prop_tgt:`DOTNET_TARGET_FRAMEWORK` property	of
+targets.
+
+This variable is used to initialize the
+:prop_tgt:`DOTNET_TARGET_FRAMEWORK` property on all targets. See that
+target property for additional information.
+
+Setting ``CMAKE_DOTNET_TARGET_FRAMEWORK`` may be necessary
+when working with ``C#`` and newer .NET framework versions to
+avoid referencing errors with the ``ALL_BUILD`` CMake target.
+
+This variable is only evaluated for :ref:`Visual Studio Generators`
+VS 2010 and above.
diff --git a/Help/variable/CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION.rst b/Help/variable/CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION.rst
index 124fefe..c2eef9e 100644
--- a/Help/variable/CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION.rst
+++ b/Help/variable/CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION.rst
@@ -6,7 +6,11 @@
 
 This variable is used to initialize the
 :prop_tgt:`DOTNET_TARGET_FRAMEWORK_VERSION` property on all
-targets. See that target property for additional information.
+targets. See that target property for additional information. When set,
+:variable:`CMAKE_DOTNET_TARGET_FRAMEWORK` takes precednece over this
+variable. See that variable or the associated target property
+:prop_tgt:`DOTNET_TARGET_FRAMEWORK` for additional information.
+
 
 Setting ``CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION`` may be necessary
 when working with ``C#`` and newer .NET framework versions to
diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake
index f7f68ca..6a40ace 100644
--- a/Modules/FindCUDAToolkit.cmake
+++ b/Modules/FindCUDAToolkit.cmake
@@ -408,6 +408,11 @@
     The path to the CUDA Toolkit library directory that contains the CUDA
     Runtime library ``cudart``.
 
+``CUDAToolkit_TARGET_DIR``
+    The path to the CUDA Toolkit directory including the target architecture
+    when cross-compiling. When not cross-compiling this will be equivalant to
+    ``CUDAToolkit_ROOT_DIR``.
+
 ``CUDAToolkit_NVCC_EXECUTABLE``
     The path to the NVIDIA CUDA compiler ``nvcc``.  Note that this path may
     **not** be the same as
@@ -641,8 +646,37 @@
 
 get_filename_component(CUDAToolkit_ROOT_DIR ${CUDAToolkit_BIN_DIR} DIRECTORY ABSOLUTE)
 
-# Now that we have the real ROOT_DIR, find components inside it.
-list(APPEND CMAKE_PREFIX_PATH ${CUDAToolkit_ROOT_DIR})
+# Handle cross compilation
+if(CMAKE_CROSSCOMPILING)
+  if(CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7-a")
+    # Support for NVPACK
+    set (CUDAToolkit_TARGET_NAME "armv7-linux-androideabi")
+  elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm")
+    # Support for arm cross compilation
+    set(CUDAToolkit_TARGET_NAME "armv7-linux-gnueabihf")
+  elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
+    # Support for aarch64 cross compilation
+    if (ANDROID_ARCH_NAME STREQUAL "arm64")
+      set(CUDAToolkit_TARGET_NAME "aarch64-linux-androideabi")
+    else()
+      set(CUDAToolkit_TARGET_NAME "aarch64-linux")
+    endif (ANDROID_ARCH_NAME STREQUAL "arm64")
+  elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+      set(CUDAToolkit_TARGET_NAME "x86_64-linux")
+  endif()
+
+  if (EXISTS "${CUDAToolkit_ROOT_DIR}/targets/${CUDAToolkit_TARGET_NAME}")
+    set(CUDAToolkit_TARGET_DIR "${CUDAToolkit_ROOT_DIR}/targets/${CUDAToolkit_TARGET_NAME}")
+    # add known CUDA target root path to the set of directories we search for programs, libraries and headers
+    list(APPEND CMAKE_FIND_ROOT_PATH "${CUDAToolkit_TARGET_DIR}")
+  endif()
+else()
+  # Not cross compiling
+  set(CUDAToolkit_TARGET_DIR "${CUDAToolkit_ROOT_DIR}")
+  # Now that we have the real ROOT_DIR, find components inside it.
+  list(APPEND CMAKE_PREFIX_PATH ${CUDAToolkit_ROOT_DIR})
+endif()
+
 
 # Find the include/ directory
 find_path(CUDAToolkit_INCLUDE_DIR
@@ -652,14 +686,20 @@
 # And find the CUDA Runtime Library libcudart
 find_library(CUDA_CUDART
   NAMES cudart
-  PATH_SUFFIXES lib64 lib/x64
+  PATH_SUFFIXES lib64 lib64/stubs lib/x64
 )
 if (NOT CUDA_CUDART AND NOT CUDAToolkit_FIND_QUIETLY)
   message(STATUS "Unable to find cudart library.")
 endif()
 
 unset(CUDAToolkit_ROOT_DIR)
-list(REMOVE_AT CMAKE_PREFIX_PATH -1)
+if(CMAKE_CROSSCOMPILING)
+  if(CUDAToolkit_TARGET_DIR)
+    list(REMOVE_AT CMAKE_FIND_ROOT_PATH -1)
+  endif()
+else()
+  list(REMOVE_AT CMAKE_PREFIX_PATH -1)
+endif()
 
 #-----------------------------------------------------------------------------
 # Perform version comparison and validate all required variables are set.
@@ -696,7 +736,7 @@
       NAMES ${search_names}
       PATHS ${CUDAToolkit_LIBRARY_DIR}
             ENV CUDA_PATH
-      PATH_SUFFIXES nvidia/current lib64 lib/x64 lib
+      PATH_SUFFIXES nvidia/current lib64 lib64/stubs lib/x64 lib lib/stubs
     )
 
     if (NOT CUDA::${lib_name} AND CUDA_${lib_name}_LIBRARY)
diff --git a/Modules/FindPackageHandleStandardArgs.cmake b/Modules/FindPackageHandleStandardArgs.cmake
index a7c3eae..a078049 100644
--- a/Modules/FindPackageHandleStandardArgs.cmake
+++ b/Modules/FindPackageHandleStandardArgs.cmake
@@ -289,10 +289,12 @@
   string(TOLOWER ${_NAME} _NAME_LOWER)
 
   if(FPHSA_FOUND_VAR)
-    if(FPHSA_FOUND_VAR MATCHES "^${_NAME}_FOUND$"  OR  FPHSA_FOUND_VAR MATCHES "^${_NAME_UPPER}_FOUND$")
+    set(_FOUND_VAR_UPPER ${_NAME_UPPER}_FOUND)
+    set(_FOUND_VAR_MIXED ${_NAME}_FOUND)
+    if(FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_MIXED  OR  FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_UPPER)
       set(_FOUND_VAR ${FPHSA_FOUND_VAR})
     else()
-      message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_NAME}_FOUND\" and \"${_NAME_UPPER}_FOUND\" are valid names.")
+      message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_FOUND_VAR_MIXED}\" and \"${_FOUND_VAR_UPPER}\" are valid names.")
     endif()
   else()
     set(_FOUND_VAR ${_NAME_UPPER}_FOUND)
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 47b13af..103129a 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
 set(CMake_VERSION_MINOR 16)
-set(CMake_VERSION_PATCH 20200123)
+set(CMake_VERSION_PATCH 20200124)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 020d2af..2dd89e3 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -549,10 +549,19 @@
     }
   };
 
-  if (this->NinjaSupportsCleanDeadTool) {
+  // The `cleandead` tool needs to know about all outputs in the build we just
+  // wrote out. Ninja-Multi doesn't have a single `build.ninja` we can use that
+  // is the union of all generated configurations, so we can't run it reliably
+  // in that case.
+  if (this->NinjaSupportsCleanDeadTool && !this->IsMultiConfig()) {
     run_ninja_tool({ "cleandead" });
   }
-  if (this->NinjaSupportsUnconditionalRecompactTool) {
+  // The `recompact` tool loads the manifest. As above, we don't have a single
+  // `build.ninja` to load for this in Ninja-Multi. This may be relaxed in the
+  // future pending further investigation into how Ninja works upstream
+  // (ninja#1721).
+  if (this->NinjaSupportsUnconditionalRecompactTool &&
+      !this->IsMultiConfig()) {
     run_ninja_tool({ "recompact" });
   }
   if (this->NinjaSupportsRestatTool) {
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 668a27d..59995be 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -340,6 +340,9 @@
       for (std::string const& arg : args) {
         val["args"].append(arg);
       }
+      val["time"] = cmSystemTools::GetTime();
+      val["frame"] =
+        static_cast<std::uint64_t>(this->ExecutionStatusStack.size());
       msg << Json::writeString(builder, val);
 #endif
       break;
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index a0b3138..d46bf56 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -517,6 +517,7 @@
   }
 
   if (impl->TargetType <= cmStateEnums::UTILITY) {
+    initProp("DOTNET_TARGET_FRAMEWORK");
     initProp("DOTNET_TARGET_FRAMEWORK_VERSION");
   }
 
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index f707bb4..fd94bc9 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -483,23 +483,33 @@
       }
       e1.Element("ProjectName", projLabel);
       {
-        // TODO: add deprecation warning for VS_* property?
-        const char* targetFrameworkVersion =
-          this->GeneratorTarget->GetProperty(
-            "VS_DOTNET_TARGET_FRAMEWORK_VERSION");
-        if (!targetFrameworkVersion) {
-          targetFrameworkVersion = this->GeneratorTarget->GetProperty(
-            "DOTNET_TARGET_FRAMEWORK_VERSION");
-        }
-        if (!targetFrameworkVersion && this->ProjectType == csproj &&
-            this->GlobalGenerator->TargetsWindowsCE() &&
-            this->GlobalGenerator->GetVersion() ==
-              cmGlobalVisualStudioGenerator::VS12) {
-          // VS12 .NETCF default to .NET framework 3.9
-          targetFrameworkVersion = "v3.9";
-        }
-        if (targetFrameworkVersion) {
-          e1.Element("TargetFrameworkVersion", targetFrameworkVersion);
+        const char* targetFramework =
+          this->GeneratorTarget->GetProperty("DOTNET_TARGET_FRAMEWORK");
+        if (targetFramework) {
+          if (std::strchr(targetFramework, ';') != nullptr) {
+            e1.Element("TargetFrameworks", targetFramework);
+          } else {
+            e1.Element("TargetFramework", targetFramework);
+          }
+        } else {
+          // TODO: add deprecation warning for VS_* property?
+          const char* targetFrameworkVersion =
+            this->GeneratorTarget->GetProperty(
+              "VS_DOTNET_TARGET_FRAMEWORK_VERSION");
+          if (!targetFrameworkVersion) {
+            targetFrameworkVersion = this->GeneratorTarget->GetProperty(
+              "DOTNET_TARGET_FRAMEWORK_VERSION");
+          }
+          if (!targetFrameworkVersion && this->ProjectType == csproj &&
+              this->GlobalGenerator->TargetsWindowsCE() &&
+              this->GlobalGenerator->GetVersion() ==
+                cmGlobalVisualStudioGenerator::VS12) {
+            // VS12 .NETCF default to .NET framework 3.9
+            targetFrameworkVersion = "v3.9";
+          }
+          if (targetFrameworkVersion) {
+            e1.Element("TargetFrameworkVersion", targetFrameworkVersion);
+          }
         }
         if (this->ProjectType == vcxproj &&
             this->GlobalGenerator->TargetsWindowsCE()) {
@@ -4110,6 +4120,9 @@
     e2.Element("Project", "{" + this->GlobalGenerator->GetGUID(name) + "}");
     e2.Element("Name", name);
     this->WriteDotNetReferenceCustomTags(e2, name);
+    if (dt->IsCSharpOnly() || cmHasLiteralSuffix(path, "csproj")) {
+      e2.Element("SkipGetTargetFrameworkProperties", "true");
+    }
 
     // Don't reference targets that don't produce any output.
     if (dt->GetManagedType("") == cmGeneratorTarget::ManagedType::Undefined) {
diff --git a/Tests/RunCMake/CommandLine/trace-json-v1-check.py b/Tests/RunCMake/CommandLine/trace-json-v1-check.py
index 14febaf..e617b76 100755
--- a/Tests/RunCMake/CommandLine/trace-json-v1-check.py
+++ b/Tests/RunCMake/CommandLine/trace-json-v1-check.py
@@ -46,6 +46,7 @@
     {
         'args': msg_args,
         'cmd': 'message',
+        'frame': 3 if expand else 2
     },
 ]
 
@@ -59,14 +60,17 @@
 
     for i in fp.readlines():
         line = json.loads(i)
-        assert sorted(line.keys()) == ['args', 'cmd', 'file', 'line']
+        assert sorted(line.keys()) == ['args', 'cmd', 'file', 'frame', 'line', 'time']
         assert isinstance(line['args'], list)
         assert isinstance(line['cmd'], unicode)
         assert isinstance(line['file'], unicode)
+        assert isinstance(line['frame'], int)
         assert isinstance(line['line'], int)
+        assert isinstance(line['time'], float)
 
         for j in required_traces:
-            if j['cmd'] == line['cmd'] and j['args'] == line['args']:
-                j['found'] = True
+            # Compare the subset of required keys with line
+            if {k: line[k] for k in j} == j:
+                required_traces.remove(j)
 
-assert all([x.get('found', False) == True for x in required_traces])
+assert not required_traces
diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
index 5cbe333..8a04f78 100644
--- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
@@ -53,3 +53,6 @@
 else()
   run_cmake(UnityBuildNative)
 endif()
+
+run_cmake(VsDotnetTargetFramework)
+run_cmake(VsDotnetTargetFrameworkVersion)
diff --git a/Tests/RunCMake/VS10Project/VSDotnetTargetFrameworkVersion.cmake b/Tests/RunCMake/VS10Project/VSDotnetTargetFrameworkVersion.cmake
new file mode 100644
index 0000000..8e0e0b4
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VSDotnetTargetFrameworkVersion.cmake
@@ -0,0 +1,10 @@
+enable_language(CSharp)
+if(NOT CMAKE_CSharp_COMPILER)
+  return()
+endif()
+
+set(CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION "v4.6.1")
+add_library(foo SHARED foo.cs)
+
+set(CMAKE_DOTNET_TARGET_FRAMEWORK "netcoreapp3.1")
+add_library(bar SHARED foo.cs)
diff --git a/Tests/RunCMake/VS10Project/VsDotnetTargetFramework-check.cmake b/Tests/RunCMake/VS10Project/VsDotnetTargetFramework-check.cmake
new file mode 100644
index 0000000..e656639
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsDotnetTargetFramework-check.cmake
@@ -0,0 +1,40 @@
+set(files foo.csproj bar.csproj)
+
+set(inLib1 FALSE)
+set(targetFrameworkInLib1 FALSE)
+
+set(inLib2 FALSE)
+set(targetFrameworksInLib2 FALSE)
+
+foreach(file ${files})
+  set(csProjectFile ${RunCMake_TEST_BINARY_DIR}/${file})
+
+  if(NOT EXISTS "${csProjectFile}")
+    set(RunCMake_TEST_FAILED "Project file ${csProjectFile} does not exist.")
+    return()
+  endif()
+
+  file(STRINGS "${csProjectFile}" lines)
+
+  foreach(line IN LISTS lines)
+    if(NOT inLib1)
+      if(line MATCHES " *<TargetFramework>netcoreapp3.1</TargetFramework> *$")
+        set(targetFrameworkInLib1  TRUE)
+        set(inLib1  TRUE)
+      endif()
+    elseif(NOT inLib2)
+      if(line MATCHES " *<TargetFrameworks>netcoreapp3.1;net461</TargetFrameworks> *$")
+        set(targetFrameworksInLib2  TRUE)
+        set(inLib2 TRUE)
+      endif()
+    endif()
+  endforeach()
+endforeach()
+
+if(NOT targetFrameworkInLib1)
+  set(RunCMake_TEST_FAILED "TargetFramework not set correctly.")
+endif()
+
+if(NOT targetFrameworksInLib2)
+  set(RunCMake_TEST_FAILED "TargetFrameworks not set correctly.")
+endif()
diff --git a/Tests/RunCMake/VS10Project/VsDotnetTargetFramework.cmake b/Tests/RunCMake/VS10Project/VsDotnetTargetFramework.cmake
new file mode 100644
index 0000000..f553679
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsDotnetTargetFramework.cmake
@@ -0,0 +1,11 @@
+enable_language(CSharp)
+if(NOT CMAKE_CSharp_COMPILER)
+    return()
+endif()
+
+set(CMAKE_DOTNET_TARGET_FRAMEWORK "netcoreapp3.1")
+set(CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION "net461")
+add_library(foo SHARED foo.cs)
+
+set(CMAKE_DOTNET_TARGET_FRAMEWORK "netcoreapp3.1;net461")
+add_library(bar SHARED foo.cs)
diff --git a/Tests/RunCMake/VS10Project/VsDotnetTargetFrameworkVersion-check.cmake b/Tests/RunCMake/VS10Project/VsDotnetTargetFrameworkVersion-check.cmake
new file mode 100644
index 0000000..d2c3c3b
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsDotnetTargetFrameworkVersion-check.cmake
@@ -0,0 +1,40 @@
+set(files foo.csproj bar.csproj)
+
+set(inLib1 FALSE)
+set(targetFrameworkInLib1 FALSE)
+
+set(inLib2 FALSE)
+set(targetFrameworksInLib2 FALSE)
+
+foreach(file ${files})
+  set(csProjectFile ${RunCMake_TEST_BINARY_DIR}/${file})
+
+  if(NOT EXISTS "${csProjectFile}")
+    set(RunCMake_TEST_FAILED "Project file ${csProjectFile} does not exist.")
+    return()
+  endif()
+
+  file(STRINGS "${csProjectFile}" lines)
+
+  foreach(line IN LISTS lines)
+    if(NOT inLib1)
+      if(line MATCHES " *<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> *$")
+        set(targetFrameworkInLib1  TRUE)
+        set(inLib1  TRUE)
+      endif()
+    elseif(NOT inLib2)
+      if(line MATCHES " *<TargetFramework>netcoreapp3.1</TargetFramework> *$")
+        set(targetFrameworksInLib2  TRUE)
+        set(inLib2 TRUE)
+      endif()
+    endif()
+  endforeach()
+endforeach()
+
+if(NOT targetFrameworkInLib1)
+  set(RunCMake_TEST_FAILED "TargetFrameworkVersion not set correctly.")
+endif()
+
+if(NOT targetFrameworksInLib2)
+  set(RunCMake_TEST_FAILED "TargetFramework not set correctly.")
+endif()
diff --git a/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake b/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake
index 47dac34..7ed0773 100644
--- a/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake
+++ b/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake
@@ -5,3 +5,7 @@
 run_cmake(CustomTypePlatform)
 run_cmake(CustomGuidTypePlatform)
 run_cmake(CustomConfig)
+
+if(RunCMake_GENERATOR MATCHES "Visual Studio ([^9]|9[0-9])")
+  run_cmake(SkipGetTargetFrameworkProperties)
+endif()
diff --git a/Tests/RunCMake/include_external_msproject/SkipGetTargetFrameworkProperties-check.cmake b/Tests/RunCMake/include_external_msproject/SkipGetTargetFrameworkProperties-check.cmake
new file mode 100644
index 0000000..375b231
--- /dev/null
+++ b/Tests/RunCMake/include_external_msproject/SkipGetTargetFrameworkProperties-check.cmake
@@ -0,0 +1,21 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/ALL_BUILD.vcxproj" all_build)
+
+macro(project_reference EXTERNAL_PROJECT)
+  string(REGEX MATCH
+    "<ProjectReference.Include=.${${EXTERNAL_PROJECT}}.>.*</SkipGetTargetFrameworkProperties>"
+    EndOfProjectReference
+    ${all_build}
+    )
+endmacro()
+
+set(external_project "external.project")
+project_reference(external_project)
+if(NOT ${EndOfProjectReference} MATCHES ".*</ProjectReference>")
+  set(RunCMake_TEST_FAILED "${test} is being set unexpectedly.")
+endif()
+
+set(external_project "external.csproj")
+project_reference(external_project)
+if(${EndOfProjectReference} MATCHES ".*</ProjectReference>")
+  set(RunCMake_TEST_FAILED "${test} is not set.")
+endif()
diff --git a/Tests/RunCMake/include_external_msproject/SkipGetTargetFrameworkProperties.cmake b/Tests/RunCMake/include_external_msproject/SkipGetTargetFrameworkProperties.cmake
new file mode 100644
index 0000000..f660bd0
--- /dev/null
+++ b/Tests/RunCMake/include_external_msproject/SkipGetTargetFrameworkProperties.cmake
@@ -0,0 +1,5 @@
+include_external_msproject(external1 external.project
+                           GUID aaa-bbb-ccc-000)
+
+include_external_msproject(external2 external.csproj
+                           GUID aaa-bbb-ccc-001)