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
This commit is contained in:
Kinan Mahdi
2020-02-18 17:06:56 +01:00
committed by Brad King
parent 8625ffd939
commit ac6b18cd90
12 changed files with 123 additions and 88 deletions

View File

@@ -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(

View File

@@ -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;

View File

@@ -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)

View File

@@ -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;
}
}

View File

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

View File

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

View File

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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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})

View File

@@ -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()