Merge topic 'vs-dotnet-standard-core'

ae1e1909a1 VS: Add support for .NET Standard and .NET Core

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !4240
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 da2b06e..bac8415 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/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/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/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)