Browse Source

VS: Support C# project references

When specifying a pure C# target in the `target_link_libraries()` call to
another C++ target, a `<ProjectReference>` was setup for it (we wanted this)
but also a corresponding `.lib` was added under `<AdditionalDependencies>`
(we didn't want this).

This change introduces a check that prevents `.lib` linker options from
being used when the corresponding target for that library is a C# target.

Fixes: #17678
Robert Dailey 7 years ago
parent
commit
076a356cd1

+ 16 - 18
Source/cmGlobalVisualStudioGenerator.cxx

@@ -737,26 +737,24 @@ bool cmGlobalVisualStudioGenerator::TargetIsFortranOnly(
 bool cmGlobalVisualStudioGenerator::TargetIsCSharpOnly(
   cmGeneratorTarget const* gt)
 {
-  // check to see if this is a C# build
-  std::set<std::string> languages;
-  {
-    // Issue diagnostic if the source files depend on the config.
-    std::vector<cmSourceFile*> sources;
-    if (!gt->GetConfigCommonSourceFiles(sources)) {
-      return false;
-    }
-    // Only "real" targets are allowed to be C# targets.
-    if (gt->Target->GetType() > cmStateEnums::OBJECT_LIBRARY) {
-      return false;
-    }
+  // C# targets can be defined with add_library() (using SHARED or STATIC) and
+  // also using add_executable(). We do not treat imported C# targets the same
+  // (these come in as UTILITY)
+  if (gt->GetType() != cmStateEnums::SHARED_LIBRARY &&
+      gt->GetType() != cmStateEnums::STATIC_LIBRARY &&
+      gt->GetType() != cmStateEnums::EXECUTABLE) {
+    return false;
   }
-  gt->GetLanguages(languages, "");
-  if (languages.size() == 1) {
-    if (*languages.begin() == "CSharp") {
-      return true;
-    }
+
+  // Issue diagnostic if the source files depend on the config.
+  std::vector<cmSourceFile*> sources;
+  if (!gt->GetConfigCommonSourceFiles(sources)) {
+    return false;
   }
-  return false;
+
+  std::set<std::string> languages;
+  gt->GetLanguages(languages, "");
+  return languages.size() == 1 && languages.count("CSharp") > 0;
 }
 
 bool cmGlobalVisualStudioGenerator::TargetCanBeReferenced(

+ 11 - 0
Source/cmVisualStudio10TargetGenerator.cxx

@@ -3451,6 +3451,17 @@ void cmVisualStudio10TargetGenerator::AddLibraries(
   std::string currentBinDir =
     this->LocalGenerator->GetCurrentBinaryDirectory();
   for (cmComputeLinkInformation::Item const& l : libs) {
+    // Do not allow C# targets to be added to the LIB listing. LIB files are
+    // used for linking C++ dependencies. C# libraries do not have lib files.
+    // Instead, they compile down to C# reference libraries (DLL files). The
+    // `<ProjectReference>` elements added to the vcxproj are enough for the
+    // IDE to deduce the DLL file required by other C# projects that need its
+    // reference library.
+    if (l.Target &&
+        cmGlobalVisualStudioGenerator::TargetIsCSharpOnly(l.Target)) {
+      continue;
+    }
+
     if (l.IsPath) {
       std::string path =
         this->LocalGenerator->ConvertToRelativePath(currentBinDir, l.Value);

+ 1 - 0
Tests/CMakeLists.txt

@@ -348,6 +348,7 @@ if(BUILD_TESTING)
   if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^89]|[89][0-9])")
     ADD_TEST_MACRO(CSharpOnly CSharpOnly)
     ADD_TEST_MACRO(CSharpLinkToCxx CSharpLinkToCxx)
+    ADD_TEST_MACRO(CSharpLinkFromCxx CSharpLinkFromCxx)
   endif()
 
   ADD_TEST_MACRO(COnly COnly)

+ 1 - 0
Tests/CSharpLinkFromCxx/.gitattributes

@@ -0,0 +1 @@
+UsefulManagedCppClass.* -format.clang-format

+ 19 - 0
Tests/CSharpLinkFromCxx/CMakeLists.txt

@@ -0,0 +1,19 @@
+# Take a C# shared library and link it to a managed C++ shared library
+cmake_minimum_required(VERSION 3.10)
+project (CSharpLinkFromCxx CXX CSharp)
+
+add_library(CSharpLibrary SHARED UsefulCSharpClass.cs)
+
+# we have to change the default flags for the
+# managed C++ project to build
+string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
+string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
+
+# The C# project is a dependency of the C++/CLI project
+add_library(ManagedCppLibrary SHARED UsefulManagedCppClass.cpp UsefulManagedCppClass.hpp)
+target_compile_options(ManagedCppLibrary PRIVATE "/clr")
+target_link_libraries(ManagedCppLibrary PUBLIC CSharpLibrary)
+
+# Main executable for the test framework
+add_executable(CSharpLinkFromCxx CSharpLinkFromCxx.cs)
+target_link_libraries(CSharpLinkFromCxx PRIVATE ManagedCppLibrary)

+ 16 - 0
Tests/CSharpLinkFromCxx/CSharpLinkFromCxx.cs

@@ -0,0 +1,16 @@
+using System;
+using CSharpLibrary;
+
+namespace CSharpLinkFromCxx
+{
+    internal class CSharpLinkFromCxx
+    {
+        public static void Main(string[] args)
+        {
+            Console.WriteLine("Starting test for CSharpLinkFromCxx");
+
+            var useful = new UsefulManagedCppClass();
+            useful.RunTest();
+        }
+    }
+}

+ 12 - 0
Tests/CSharpLinkFromCxx/UsefulCSharpClass.cs

@@ -0,0 +1,12 @@
+using System;
+
+namespace CSharpLibrary
+{
+    public class UsefulCSharpClass
+    {
+        public string GetSomethingUseful()
+        {
+            return "Something Useful";
+        }
+    }
+}

+ 15 - 0
Tests/CSharpLinkFromCxx/UsefulManagedCppClass.cpp

@@ -0,0 +1,15 @@
+#include "UsefulManagedCppClass.hpp"
+
+namespace CSharpLibrary
+{
+    UsefulManagedCppClass::UsefulManagedCppClass()
+    {
+        auto useful = gcnew UsefulCSharpClass();
+        m_usefulString = useful->GetSomethingUseful();
+    }
+
+    void UsefulManagedCppClass::RunTest()
+    {
+        Console::WriteLine("Printing from Managed CPP Class: " + m_usefulString);
+    }
+}

+ 16 - 0
Tests/CSharpLinkFromCxx/UsefulManagedCppClass.hpp

@@ -0,0 +1,16 @@
+using namespace System;
+
+namespace CSharpLibrary
+{
+    public ref class UsefulManagedCppClass
+    {
+    public:
+
+        UsefulManagedCppClass();
+        void RunTest();
+
+    private:
+
+        String^ m_usefulString;
+    };
+}