From 90cb40832375fa880b792ca9bf5cdd3071912f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20St=C3=BCrmer?= Date: Tue, 17 Jan 2017 16:09:38 +0100 Subject: VS: improve handling of source files with special extensions in .csproj Mainly and tags are added to connect generated and manually edited files. Special file extensions that are take care of are: - .Designer.cs - .xaml.cs - .settings - .resx - .xaml --- Help/manual/cmake-properties.7.rst | 3 + Help/prop_sf/VS_COPY_TO_OUT_DIR.rst | 6 + Help/prop_sf/VS_INCLUDE_IN_VSIX.rst | 6 + Help/prop_sf/VS_RESOURCE_GENERATOR.rst | 8 + Help/release/dev/vs-advanced-source-properties.rst | 12 ++ Help/release/dev/vs-csharp-support.rst | 13 +- Source/cmVisualStudio10TargetGenerator.cxx | 203 +++++++++++++++++++-- 7 files changed, 230 insertions(+), 21 deletions(-) create mode 100644 Help/prop_sf/VS_COPY_TO_OUT_DIR.rst create mode 100644 Help/prop_sf/VS_INCLUDE_IN_VSIX.rst create mode 100644 Help/prop_sf/VS_RESOURCE_GENERATOR.rst create mode 100644 Help/release/dev/vs-advanced-source-properties.rst diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 864d1dc..03178a1 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -362,8 +362,11 @@ Properties on Source Files /prop_sf/SKIP_AUTORCC /prop_sf/SKIP_AUTOUIC /prop_sf/SYMBOLIC + /prop_sf/VS_COPY_TO_OUT_DIR /prop_sf/VS_DEPLOYMENT_CONTENT /prop_sf/VS_DEPLOYMENT_LOCATION + /prop_sf/VS_INCLUDE_IN_VSIX + /prop_sf/VS_RESOURCE_GENERATOR /prop_sf/VS_SHADER_ENTRYPOINT /prop_sf/VS_SHADER_FLAGS /prop_sf/VS_SHADER_MODEL diff --git a/Help/prop_sf/VS_COPY_TO_OUT_DIR.rst b/Help/prop_sf/VS_COPY_TO_OUT_DIR.rst new file mode 100644 index 0000000..16c8d83 --- /dev/null +++ b/Help/prop_sf/VS_COPY_TO_OUT_DIR.rst @@ -0,0 +1,6 @@ +VS_COPY_TO_OUT_DIR +------------------ + +Sets the ```` tag for a source file in a +Visual Studio project file. Valid values are ``Never``, ``Always`` +and ``PreserveNewest``. diff --git a/Help/prop_sf/VS_INCLUDE_IN_VSIX.rst b/Help/prop_sf/VS_INCLUDE_IN_VSIX.rst new file mode 100644 index 0000000..30f471d --- /dev/null +++ b/Help/prop_sf/VS_INCLUDE_IN_VSIX.rst @@ -0,0 +1,6 @@ +VS_INCLUDE_IN_VSIX +------------------ + +Boolean property to specify if the file should be included within a VSIX +extension package. This is needed for development of Visual Studio +extensions. diff --git a/Help/prop_sf/VS_RESOURCE_GENERATOR.rst b/Help/prop_sf/VS_RESOURCE_GENERATOR.rst new file mode 100644 index 0000000..97e5aac --- /dev/null +++ b/Help/prop_sf/VS_RESOURCE_GENERATOR.rst @@ -0,0 +1,8 @@ +VS_RESOURCE_GENERATOR +--------------------- + +This property allows to specify the resource generator to be used +on this file. It defaults to ``PublicResXFileCodeGenerator`` if +not set. + +This property only applies to C# projects. diff --git a/Help/release/dev/vs-advanced-source-properties.rst b/Help/release/dev/vs-advanced-source-properties.rst new file mode 100644 index 0000000..dd391e0 --- /dev/null +++ b/Help/release/dev/vs-advanced-source-properties.rst @@ -0,0 +1,12 @@ +vs-advanced-source-properties +----------------------------- + +* The :ref:`Visual Studio Generators` for VS 2010 and above + learned some more source file properties: + + - :prop_sf:`VS_RESOURCE_GENERATOR` (C# only): allows setting the resource + generator + - :prop_sf:`VS_COPY_TO_OUT_DIR`: parameter to set if file should be copied + to output directory (values: ``Never``, ``Always``, ``PreserveNewest``) + - :prop_sf:`VS_INCLUDE_IN_VSIX`: boolean property to include file include + Visual Studio extension package diff --git a/Help/release/dev/vs-csharp-support.rst b/Help/release/dev/vs-csharp-support.rst index 26d8574..46b235a 100644 --- a/Help/release/dev/vs-csharp-support.rst +++ b/Help/release/dev/vs-csharp-support.rst @@ -19,7 +19,16 @@ vs-native-csharp-support that specifically targets C# contains ``CSharp`` as a part of their names. -* More finetuning of C# targets can be done using target - properties. Specifically the Visual Studio related target +* More finetuning of C# targets can be done using target and source + file properties. Specifically the Visual Studio related target properties (``VS_*``) are worth a look (for setting toolset versions, root namespaces, assembly icons, ...). + +* **Auto-"linking"** in .csproj files: In C#/.NET development with + Visual Studio there is a number of visual editors used which + generate code. Both the generated files and the ones edited + with the UI are connected in the ``.csproj`` file using + ```` tags. If CMake finds within a C# project + any source file with extension ``.Designer.cs`` or ``.xaml.cs``, + it checks sibling files with extension ``.xaml``, ``.settings``, + ``.resx`` or ``.cs`` and establishes the dependency connection. diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 89380eb..138ac2b 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -610,30 +610,91 @@ void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup() this->GeneratorTarget->GetResxSources(resxObjs, ""); if (!resxObjs.empty()) { this->WriteString("\n", 1); + std::string srcDir = this->Makefile->GetCurrentSourceDirectory(); + this->ConvertToWindowsSlash(srcDir); for (std::vector::const_iterator oi = resxObjs.begin(); oi != resxObjs.end(); ++oi) { std::string obj = (*oi)->GetFullPath(); this->WriteString("ConvertToWindowsSlash(obj); + bool useRelativePath = false; + if (csproj == this->ProjectType && this->InSourceBuild) { + // If we do an in-source build and the resource file is in a + // subdirectory + // of the .csproj file, we have to use relative pathnames, otherwise + // visual studio does not show the file in the IDE. Sorry. + if (obj.find(srcDir) == 0) { + obj = this->ConvertPath(obj, true); + this->ConvertToWindowsSlash(obj); + useRelativePath = true; + } + } (*this->BuildFileStream) << obj << "\">\n"; - this->WriteString("", 3); - std::string hFileName = obj.substr(0, obj.find_last_of(".")) + ".h"; - (*this->BuildFileStream) << hFileName << "\n"; - - for (std::vector::const_iterator i = - this->Configurations.begin(); - i != this->Configurations.end(); ++i) { - this->WritePlatformConfigTag("LogicalName", i->c_str(), 3); - if (this->GeneratorTarget->GetProperty("VS_GLOBAL_ROOTNAMESPACE") || - // Handle variant of VS_GLOBAL_ for RootNamespace. - this->GeneratorTarget->GetProperty("VS_GLOBAL_RootNamespace")) { - (*this->BuildFileStream) << "$(RootNamespace)."; + if (csproj != this->ProjectType) { + this->WriteString("", 3); + std::string hFileName = obj.substr(0, obj.find_last_of(".")) + ".h"; + (*this->BuildFileStream) << hFileName << "\n"; + + for (std::vector::const_iterator i = + this->Configurations.begin(); + i != this->Configurations.end(); ++i) { + this->WritePlatformConfigTag("LogicalName", i->c_str(), 3); + if (this->GeneratorTarget->GetProperty("VS_GLOBAL_ROOTNAMESPACE") || + // Handle variant of VS_GLOBAL_ for RootNamespace. + this->GeneratorTarget->GetProperty("VS_GLOBAL_RootNamespace")) { + (*this->BuildFileStream) << "$(RootNamespace)."; + } + (*this->BuildFileStream) << "%(Filename)"; + (*this->BuildFileStream) << ".resources"; + (*this->BuildFileStream) << "\n"; + } + } else { + std::string binDir = this->Makefile->GetCurrentBinaryDirectory(); + this->ConvertToWindowsSlash(binDir); + // 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 { + link = cmsys::SystemTools::GetFilenameName(obj); + } + if (!link.empty()) { + this->WriteString("", 3); + (*this->BuildFileStream) << link << "\n"; + } + } + // Determine if this is a generated resource from a .Designer.cs file + std::string designerResource = + cmSystemTools::GetFilenamePath((*oi)->GetFullPath()) + "/" + + cmSystemTools::GetFilenameWithoutLastExtension( + (*oi)->GetFullPath()) + + ".Designer.cs"; + if (cmsys::SystemTools::FileExists(designerResource)) { + std::string generator = "PublicResXFileCodeGenerator"; + if (const char* g = (*oi)->GetProperty("VS_RESOURCE_GENERATOR")) { + generator = g; + } + this->WriteString("", 3); + (*this->BuildFileStream) << cmVS10EscapeXML(generator) + << "\n"; + if (designerResource.find(srcDir) == 0) { + designerResource = designerResource.substr(srcDir.length() + 1); + } else if (designerResource.find(binDir) == 0) { + designerResource = designerResource.substr(binDir.length() + 1); + } else { + designerResource = + cmsys::SystemTools::GetFilenameName(designerResource); + } + this->ConvertToWindowsSlash(designerResource); + this->WriteString("", 3); + (*this->BuildFileStream) << designerResource << "\n"; } - (*this->BuildFileStream) << "%(Filename)"; - (*this->BuildFileStream) << ".resources"; - (*this->BuildFileStream) << "\n"; } this->WriteString("\n", 2); @@ -661,6 +722,24 @@ void cmVisualStudio10TargetGenerator::WriteXamlFilesGroup() } this->WriteSource(xamlType, *oi, ">\n"); + if (csproj == this->ProjectType && !this->InSourceBuild) { + // add 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()) { + this->ConvertToWindowsSlash(link); + this->WriteString("", 3); + (*this->BuildFileStream) << link << "\n"; + } + } this->WriteString("Designer\n", 3); this->WriteString("BuildFileStream) << xamlType << ">\n"; @@ -1348,7 +1427,12 @@ void cmVisualStudio10TargetGenerator::WriteExtraSource(cmSourceFile const* sf) std::string shaderEntryPoint; std::string shaderModel; std::string shaderAdditionalFlags; + std::string settingsGenerator; + std::string settingsLastGenOutput; std::string sourceLink; + std::string subType; + std::string copyToOutDir; + std::string includeInVsix; std::string ext = cmSystemTools::LowerCase(sf->GetExtension()); if (csproj == this->ProjectType) { // EVERY extra source file must have a , otherwise it might not @@ -1405,6 +1489,28 @@ void cmVisualStudio10TargetGenerator::WriteExtraSource(cmSourceFile const* sf) tool = "XML"; } else if (ext == "natvis") { tool = "Natvis"; + } else if (ext == "settings") { + // remove path to current source dir (if files are in current source dir) + if (!sourceLink.empty()) { + settingsLastGenOutput = sourceLink; + } else { + settingsLastGenOutput = sf->GetFullPath(); + } + std::size_t pos = settingsLastGenOutput.find(".settings"); + settingsLastGenOutput.replace(pos, 9, ".Designer.cs"); + settingsGenerator = "SettingsSingleFileGenerator"; + toolHasSettings = true; + } else if (ext == "vsixmanifest") { + subType = "Designer"; + } + if (const char* c = sf->GetProperty("VS_COPY_TO_OUT_DIR")) { + copyToOutDir = c; + toolHasSettings = true; + } + if (sf->GetPropertyAsBool("VS_INCLUDE_IN_VSIX")) { + includeInVsix = "True"; + tool = "Content"; + toolHasSettings = true; } if (this->NsightTegra) { @@ -1495,10 +1601,35 @@ void cmVisualStudio10TargetGenerator::WriteExtraSource(cmSourceFile const* sf) (*this->BuildFileStream) << cmVS10EscapeXML(shaderAdditionalFlags) << "\n"; } + if (!settingsGenerator.empty()) { + this->WriteString("", 3); + (*this->BuildFileStream) << cmVS10EscapeXML(settingsGenerator) + << "\n"; + } + if (!settingsLastGenOutput.empty()) { + this->WriteString("", 3); + (*this->BuildFileStream) << cmVS10EscapeXML(settingsLastGenOutput) + << "\n"; + } if (!sourceLink.empty()) { this->WriteString("", 3); (*this->BuildFileStream) << cmVS10EscapeXML(sourceLink) << "\n"; } + if (!subType.empty()) { + this->WriteString("", 3); + (*this->BuildFileStream) << cmVS10EscapeXML(subType) << "\n"; + } + if (!copyToOutDir.empty()) { + this->WriteString("", 3); + (*this->BuildFileStream) << cmVS10EscapeXML(copyToOutDir) + << "\n"; + } + if (!includeInVsix.empty()) { + this->WriteString("", 3); + (*this->BuildFileStream) << cmVS10EscapeXML(includeInVsix) + << "\n"; + } + this->WriteString("BuildFileStream) << tool << ">\n"; } else { @@ -1815,9 +1946,43 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( sourceFileTags["Link"] = link; } } - // - // NOTE: in future commits additional props will be added! - // + // check if file is a generated .Designer.cs or .xaml.cs file + // to add additional necessary tags + const std::string fileExtension = + cmsys::SystemTools::GetFilenameExtension(f); + if (fileExtension == ".Designer.cs" || fileExtension == ".xaml.cs") { + f = f.substr(0, f.length() - fileExtension.length()); + if (sourceFileTags.find("Link") == sourceFileTags.end() && + !this->InSourceBuild) { + // add link fallback + sourceFileTags["Link"] = + cmsys::SystemTools::GetFilenameName(f) + fileExtension; + } + std::vector extensions; + extensions.push_back(".resx"); + extensions.push_back(".settings"); + extensions.push_back(".xaml"); + extensions.push_back(".cs"); + std::string dependencyExtension; + for (std::vector::iterator i = extensions.begin(); + i != extensions.end(); ++i) { + if (cmsys::SystemTools::FileExists(f + *i)) { + dependencyExtension = *i; + // There should never be more than one match. Otherwise + // one cannot tell on which match the file depends. + break; + } + } + if (dependencyExtension == ".resx") { + sourceFileTags["DesignTime"] = "True"; + sourceFileTags["AutoGen"] = "True"; + } else if (dependencyExtension == ".settings") { + sourceFileTags["DesignTimeSharedInput"] = "True"; + sourceFileTags["AutoGen"] = "True"; + } + sourceFileTags["DependentUpon"] = + cmsys::SystemTools::GetFilenameName(f) + dependencyExtension; + } // write source file specific tags if (!sourceFileTags.empty()) { hasFlags = true; @@ -1825,7 +1990,7 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( firstString = ""; for (CsPropMap::const_iterator i = sourceFileTags.begin(); i != sourceFileTags.end(); ++i) { - this->WriteString("<", 2); + this->WriteString("<", 3); (*this->BuildFileStream) << i->first << ">" << cmVS10EscapeXML(i->second) << "first << ">\n"; -- cgit v0.12