Browse Source

CSharp: Add support for source groups with out-of-source builds

This also fixes support for multiple sources of the same name in
different directories.  Add a test for both problems.

Issue: #19505
Kinan Mahdi 5 years ago
parent
commit
ac6b18cd90

+ 46 - 83
Source/cmVisualStudio10TargetGenerator.cxx

@@ -981,17 +981,11 @@ void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup(Elem& e0)
         // If the resource was NOT added using a relative path (which should
         // be the default), we have to provide a link here
         if (!useRelativePath) {
-          std::string link;
-          if (obj.find(srcDir) == 0) {
-            link = obj.substr(srcDir.length() + 1);
-          } else if (obj.find(binDir) == 0) {
-            link = obj.substr(binDir.length() + 1);
-          } else {
+          std::string link = this->GetCSharpSourceLink(oi);
+          if (link.empty()) {
             link = cmsys::SystemTools::GetFilenameName(obj);
           }
-          if (!link.empty()) {
-            e2.Element("Link", link);
-          }
+          e2.Element("Link", link);
         }
         // Determine if this is a generated resource from a .Designer.cs file
         std::string designerResource =
@@ -1054,25 +1048,6 @@ void cmVisualStudio10TargetGenerator::WriteXamlFilesGroup(Elem& e0)
       Elem e2(e1, xamlType);
       this->WriteSource(e2, oi);
       e2.SetHasElements();
-      if (this->ProjectType == csproj && !this->InSourceBuild) {
-        // add <Link> tag to written XAML source if necessary
-        const std::string& srcDir =
-          this->Makefile->GetCurrentSourceDirectory();
-        const std::string& binDir =
-          this->Makefile->GetCurrentBinaryDirectory();
-        std::string link;
-        if (obj.find(srcDir) == 0) {
-          link = obj.substr(srcDir.length() + 1);
-        } else if (obj.find(binDir) == 0) {
-          link = obj.substr(binDir.length() + 1);
-        } else {
-          link = cmsys::SystemTools::GetFilenameName(obj);
-        }
-        if (!link.empty()) {
-          ConvertToWindowsSlash(link);
-          e2.Element("Link", link);
-        }
-      }
       e2.Element("SubType", "Designer");
     }
   }
@@ -1442,13 +1417,8 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule(
   } else {
     Elem e1(e0, "ItemGroup");
     Elem e2(e1, "None");
-    std::string link;
-    this->GetCSharpSourceLink(source, link);
     this->WriteSource(e2, source);
     e2.SetHasElements();
-    if (!link.empty()) {
-      e2.Element("Link", link);
-    }
   }
   for (std::string const& c : this->Configurations) {
     cmCustomCommandGenerator ccg(command, c, lg);
@@ -1805,30 +1775,8 @@ void cmVisualStudio10TargetGenerator::WriteExtraSource(Elem& e1,
   std::string copyToOutDir;
   std::string includeInVsix;
   std::string ext = cmSystemTools::LowerCase(sf->GetExtension());
-  if (this->ProjectType == csproj) {
-    // EVERY extra source file must have a <Link>, otherwise it might not
-    // be visible in Visual Studio at all. The path relative to current
-    // source- or binary-dir is used within the link, if the file is
-    // in none of these paths, it is added with the plain filename without
-    // any path. This means the file will show up at root-level of the csproj
-    // (where CMakeLists.txt etc. are).
-    if (!this->InSourceBuild) {
-      toolHasSettings = true;
-      std::string fullFileName = sf->GetFullPath();
-      std::string srcDir = this->Makefile->GetCurrentSourceDirectory();
-      std::string binDir = this->Makefile->GetCurrentBinaryDirectory();
-      if (fullFileName.find(binDir) != std::string::npos) {
-        sourceLink.clear();
-      } else if (fullFileName.find(srcDir) != std::string::npos) {
-        sourceLink = fullFileName.substr(srcDir.length() + 1);
-      } else {
-        // fallback: add plain filename without any path
-        sourceLink = cmsys::SystemTools::GetFilenameName(fullFileName);
-      }
-      if (!sourceLink.empty()) {
-        ConvertToWindowsSlash(sourceLink);
-      }
-    }
+  if (this->ProjectType == csproj && !this->InSourceBuild) {
+    toolHasSettings = true;
   }
   if (ext == "hlsl") {
     tool = "FXCompile";
@@ -2047,9 +1995,6 @@ void cmVisualStudio10TargetGenerator::WriteExtraSource(Elem& e1,
     if (!settingsLastGenOutput.empty()) {
       e2.Element("LastGenOutput", settingsLastGenOutput);
     }
-    if (!sourceLink.empty()) {
-      e2.Element("Link", sourceLink);
-    }
     if (!subType.empty()) {
       e2.Element("SubType", subType);
     }
@@ -2102,6 +2047,20 @@ void cmVisualStudio10TargetGenerator::WriteSource(Elem& e2,
   ConvertToWindowsSlash(sourceFile);
   e2.Attribute("Include", sourceFile);
 
+  if (this->ProjectType == csproj && !this->InSourceBuild) {
+    // For out of source projects we have to provide a link (if not specified
+    // via property) for every source file (besides .cs files) otherwise they
+    // will not be visible in VS at all.
+    // First we check if the file is in a source group, then we check if the
+    // file path is relative to current source- or binary-dir, otherwise it is
+    // added with the plain filename without any path. This means the file will
+    // show up at root-level of the csproj (where CMakeLists.txt etc. are).
+    std::string link = this->GetCSharpSourceLink(sf);
+    if (link.empty())
+      link = cmsys::SystemTools::GetFilenameName(sf->GetFullPath());
+    e2.Element("Link", link);
+  }
+
   ToolSource toolSource = { sf, forceRelative };
   this->Tools[e2.Tag].push_back(toolSource);
 }
@@ -2461,12 +2420,6 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
     std::string f = source->GetFullPath();
     using CsPropMap = std::map<std::string, std::string>;
     CsPropMap sourceFileTags;
-    // set <Link> tag if necessary
-    std::string link;
-    this->GetCSharpSourceLink(source, link);
-    if (!link.empty()) {
-      sourceFileTags["Link"] = link;
-    }
     this->GetCSharpSourceProperties(&sf, sourceFileTags);
     // write source file specific tags
     if (!sourceFileTags.empty()) {
@@ -4869,24 +4822,34 @@ void cmVisualStudio10TargetGenerator::WriteCSharpSourceProperties(
   }
 }
 
-void cmVisualStudio10TargetGenerator::GetCSharpSourceLink(
-  cmSourceFile const* sf, std::string& link)
+std::string cmVisualStudio10TargetGenerator::GetCSharpSourceLink(
+  cmSourceFile const* source)
 {
-  std::string const& sourceFilePath = sf->GetFullPath();
-  std::string const& binaryDir = LocalGenerator->GetCurrentBinaryDirectory();
-
-  if (!cmSystemTools::IsSubDirectory(sourceFilePath, binaryDir)) {
-    const std::string& stripFromPath =
-      this->Makefile->GetCurrentSourceDirectory();
-    if (sourceFilePath.find(stripFromPath) == 0) {
-      if (const char* l = sf->GetProperty("VS_CSHARP_Link")) {
-        link = l;
-      } else {
-        link = sourceFilePath.substr(stripFromPath.length() + 1);
-      }
-      ConvertToWindowsSlash(link);
-    }
-  }
+  // For out of source files, we first check if a matching source group
+  // for this file exists, otherwise we check if the path relative to current
+  // source- or binary-dir is used within the link and return that
+  std::string link;
+  std::string const& fullFileName = source->GetFullPath();
+  std::string const& srcDir = this->Makefile->GetCurrentSourceDirectory();
+  std::string const& binDir = this->Makefile->GetCurrentBinaryDirectory();
+  // unfortunately we have to copy the source groups, because
+  // FindSourceGroup uses a regex which is modifying the group
+  std::vector<cmSourceGroup> sourceGroups = this->Makefile->GetSourceGroups();
+  cmSourceGroup* sourceGroup =
+    this->Makefile->FindSourceGroup(fullFileName, sourceGroups);
+  if (sourceGroup && !sourceGroup->GetFullName().empty()) {
+    link = sourceGroup->GetFullName() + "/" +
+      cmsys::SystemTools::GetFilenameName(fullFileName);
+  } else if (fullFileName.find(srcDir) == 0) {
+    link = fullFileName.substr(srcDir.length() + 1);
+  } else if (fullFileName.find(binDir) == 0) {
+    link = fullFileName.substr(binDir.length() + 1);
+  } else if (const char* l = source->GetProperty("VS_CSHARP_Link")) {
+    link = l;
+  }
+
+  ConvertToWindowsSlash(link);
+  return link;
 }
 
 std::string cmVisualStudio10TargetGenerator::GetCMakeFilePath(

+ 1 - 1
Source/cmVisualStudio10TargetGenerator.h

@@ -183,7 +183,7 @@ private:
                                  std::map<std::string, std::string>& tags);
   void WriteCSharpSourceProperties(
     Elem& e2, const std::map<std::string, std::string>& tags);
-  void GetCSharpSourceLink(cmSourceFile const* sf, std::string& link);
+  std::string GetCSharpSourceLink(cmSourceFile const* source);
 
 private:
   friend class cmVS10GeneratorOptions;

+ 3 - 1
Tests/CSharpOnly/CMakeLists.txt

@@ -2,7 +2,9 @@
 project (CSharpOnly CSharp)
 
 # C# does not make any difference between STATIC and SHARED libs
-add_library(lib1 STATIC lib1.cs)
+add_library(lib1 STATIC lib1.cs nested/lib1.cs)
+#without the source group this test will fail to compile
+source_group(nested FILES nested/lib1.cs)
 add_library(lib2 SHARED lib2.cs)
 
 add_executable(CSharpOnly csharponly.cs)

+ 1 - 3
Tests/CSharpOnly/csharponly.cs

@@ -5,10 +5,8 @@ namespace CSharpOnly
         public static void Main(string[] args)
         {
             int val = Lib1.getResult();
-
             Lib2 l = new Lib2();
-            val = l.myVal;
-
+            val = val +  l.myVal + nested.Lib1.getResult();
             return;
         }
     }

+ 13 - 0
Tests/CSharpOnly/nested/lib1.cs

@@ -0,0 +1,13 @@
+namespace CSharpOnly
+{
+    namespace nested
+    {
+        public class Lib1
+        {
+            public static int getResult()
+            {
+                return 23;
+            }
+        }
+    }
+}

+ 3 - 0
Tests/RunCMake/VS10Project/CSharpSourceGroup/foo.cs

@@ -0,0 +1,3 @@
+void foo()
+{
+}

+ 0 - 0
Tests/RunCMake/VS10Project/CSharpSourceGroup/images/empty.bmp


+ 3 - 0
Tests/RunCMake/VS10Project/CSharpSourceGroup/nested/baz.cs

@@ -0,0 +1,3 @@
+void baz()
+{
+}

+ 1 - 1
Tests/RunCMake/VS10Project/RunCMakeTest.cmake

@@ -3,12 +3,12 @@ cmake_policy(SET CMP0057 NEW)
 include(RunCMake)
 cmake_policy(SET CMP0054 NEW)
 
+run_cmake(VsCsharpSourceGroup)
 run_cmake(VsCSharpCompilerOpts)
 run_cmake(ExplicitCMakeLists)
 run_cmake(RuntimeLibrary)
 run_cmake(SourceGroupCMakeLists)
 run_cmake(SourceGroupTreeCMakeLists)
-
 run_cmake(VsConfigurationType)
 run_cmake(VsTargetsFileReferences)
 run_cmake(VsCustomProps)

+ 22 - 0
Tests/RunCMake/VS10Project/VsCsharpSourceGroup-check.cmake

@@ -0,0 +1,22 @@
+set(csProjFile "${RunCMake_TEST_BINARY_DIR}/VsCsharpSourceGroup.csproj")
+if(NOT EXISTS "${csProjFile}")
+  set(RunCMake_TEST_FAILED "Project file ${csProjFile} does not exist.")
+  return()
+endif()
+
+file(STRINGS "${csProjFile}" lines)
+
+include(${RunCMake_TEST_SOURCE_DIR}/VsCsharpSourceGroupHelpers.cmake)
+
+set(SOURCE_GROUPS_TO_FIND
+  "CSharpSourceGroup"
+  "CSharpSourceGroup/nested"
+  "Images"
+)
+
+foreach(GROUP_NAME IN LISTS ${SOURCE_GROUPS_TO_FIND})
+  find_source_group("${lines}" ${GROUP_NAME})
+  if(NOT ${SOURCE_GROUP_FOUND})
+    return()
+  endif()
+endforeach()

+ 16 - 0
Tests/RunCMake/VS10Project/VsCsharpSourceGroup.cmake

@@ -0,0 +1,16 @@
+enable_language(CSharp)
+set(CMAKE_CONFIGURATION_TYPES Debug)
+
+set(SRC_FILES
+  ${CMAKE_CURRENT_SOURCE_DIR}/CSharpSourceGroup/foo.cs
+  ${CMAKE_CURRENT_SOURCE_DIR}/CSharpSourceGroup/nested/baz.cs
+)
+
+set(IMAGE_FILES
+  ${CMAKE_CURRENT_SOURCE_DIR}/CSharpSourceGroup/Images/empty.bmp
+)
+
+add_library(VsCsharpSourceGroup SHARED ${SRC_FILES} ${IMAGE_FILES})
+source_group("CSharpSourceGroup" FILES ${CMAKE_CURRENT_SOURCE_DIR}/CSharpSourceGroup/foo.cs)
+source_group("CSharpSourceGroup/nested" FILES ${CMAKE_CURRENT_SOURCE_DIR}/CSharpSourceGroup/nested/baz.cs)
+source_group("Images" FILES ${IMAGE_FILES})

+ 15 - 0
Tests/RunCMake/VS10Project/VsCsharpSourceGroupHelpers.cmake

@@ -0,0 +1,15 @@
+function(find_source_group LINES NAME)
+  set(foundSourceGroupLink 0)
+  foreach(line IN LISTS LINES)
+    if(line MATCHES "<Link>${NAME}</Link>")
+      set(foundSourceGroupLink 1)
+    endif()
+  endforeach()
+
+  if(NOT foundSourceGroupLink)
+    set(RunCMake_TEST_FAILED "Source group link for ${NAME} not found." PARENT_SCOPE)
+    set(SOURCE_GROUP_FOUND 0 PARENT_SCOPE)
+    return()
+  endif()
+  set(SOURCE_GROUP_FOUND 1 PARENT_SCOPE)
+endfunction()