From fa76e5d194c2641ca8ee40c2053860196e8a445b Mon Sep 17 00:00:00 2001 From: Sumit Bhardwaj Date: Fri, 3 Dec 2021 14:04:27 -0800 Subject: cmVisualStudio10TargetGenerator: Factor out helper for classic MSBuild project In preparation for generating .Net SDK style project, refactor cmVisualStudio10TargetGenerato::Generate to split the functionality to write classic MSBuild project file. This commit only introduces a helper function and moves the functionality there. A later commit will add WriteSdkStyleProjectFile, the call to it, and the rest of the .Net SDK-style changes. --- Source/cmVisualStudio10TargetGenerator.cxx | 115 ++++++++++++++++------------- Source/cmVisualStudio10TargetGenerator.h | 7 ++ 2 files changed, 70 insertions(+), 52 deletions(-) diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 5bec7d3..088369c 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -405,6 +405,20 @@ void cmVisualStudio10TargetGenerator::Generate() // Write the encoding header into the file char magic[] = { char(0xEF), char(0xBB), char(0xBF) }; BuildFileStream.write(magic, 3); + + this->WriteClassicMsBuildProjectFile(BuildFileStream); + + if (BuildFileStream.Close()) { + this->GlobalGenerator->FileReplacedDuringGenerate(PathToProjectFile); + } + + // The groups are stored in a separate file for VS 10 + this->WriteGroups(); +} + +void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile( + cmGeneratedFileStream& BuildFileStream) +{ BuildFileStream << "GlobalGenerator->Encoding() << "\"?>"; { @@ -453,8 +467,7 @@ void cmVisualStudio10TargetGenerator::Generate() { Elem e1(e0, "PropertyGroup"); - e1.Attribute("Label", "Globals"); - e1.Element("ProjectGuid", "{" + this->GUID + "}"); + this->WriteCommonPropertyGroupGlobals(e1); if ((this->MSTools || this->Android) && this->GeneratorTarget->IsInBuildSystem()) { @@ -462,16 +475,6 @@ void cmVisualStudio10TargetGenerator::Generate() this->VerifyNecessaryFiles(); } - cmValue vsProjectTypes = - this->GeneratorTarget->GetProperty("VS_GLOBAL_PROJECT_TYPES"); - if (vsProjectTypes) { - const char* tagName = "ProjectTypes"; - if (this->ProjectType == VsProjectType::csproj) { - tagName = "ProjectTypeGuids"; - } - e1.Element(tagName, *vsProjectTypes); - } - cmValue vsProjectName = this->GeneratorTarget->GetProperty("VS_SCC_PROJECTNAME"); cmValue vsLocalPath = @@ -495,24 +498,6 @@ void cmVisualStudio10TargetGenerator::Generate() e1.Element("WinMDAssembly", "true"); } - cmValue vsGlobalKeyword = - this->GeneratorTarget->GetProperty("VS_GLOBAL_KEYWORD"); - if (!vsGlobalKeyword) { - if (this->GlobalGenerator->TargetsAndroid()) { - e1.Element("Keyword", "Android"); - } else { - e1.Element("Keyword", "Win32Proj"); - } - } else { - e1.Element("Keyword", *vsGlobalKeyword); - } - - cmValue vsGlobalRootNamespace = - this->GeneratorTarget->GetProperty("VS_GLOBAL_ROOTNAMESPACE"); - if (vsGlobalRootNamespace) { - e1.Element("RootNamespace", *vsGlobalRootNamespace); - } - e1.Element("Platform", this->Platform); cmValue projLabel = this->GeneratorTarget->GetProperty("PROJECT_LABEL"); e1.Element("ProjectName", projLabel ? projLabel : this->Name); @@ -602,24 +587,6 @@ void cmVisualStudio10TargetGenerator::Generate() e1.Element("VCTargetsPath", vcTargetsPath); } - std::vector keys = this->GeneratorTarget->GetPropertyKeys(); - for (std::string const& keyIt : keys) { - static const cm::string_view prefix = "VS_GLOBAL_"; - if (!cmHasPrefix(keyIt, prefix)) - continue; - cm::string_view globalKey = - cm::string_view(keyIt).substr(prefix.length()); - // Skip invalid or separately-handled properties. - if (globalKey.empty() || globalKey == "PROJECT_TYPES" || - globalKey == "ROOTNAMESPACE" || globalKey == "KEYWORD") { - continue; - } - cmValue value = this->GeneratorTarget->GetProperty(keyIt); - if (!value) - continue; - e1.Element(globalKey, *value); - } - if (this->Managed) { if (this->LocalGenerator->GetVersion() >= cmGlobalVisualStudioGenerator::VS17) { @@ -839,13 +806,57 @@ void cmVisualStudio10TargetGenerator::Generate() } } } +} - if (BuildFileStream.Close()) { - this->GlobalGenerator->FileReplacedDuringGenerate(PathToProjectFile); +void cmVisualStudio10TargetGenerator::WriteCommonPropertyGroupGlobals(Elem& e1) +{ + e1.Attribute("Label", "Globals"); + e1.Element("ProjectGuid", "{" + this->GUID + "}"); + + cmValue vsProjectTypes = + this->GeneratorTarget->GetProperty("VS_GLOBAL_PROJECT_TYPES"); + if (vsProjectTypes) { + const char* tagName = "ProjectTypes"; + if (this->ProjectType == VsProjectType::csproj) { + tagName = "ProjectTypeGuids"; + } + e1.Element(tagName, *vsProjectTypes); } - // The groups are stored in a separate file for VS 10 - this->WriteGroups(); + cmValue vsGlobalKeyword = + this->GeneratorTarget->GetProperty("VS_GLOBAL_KEYWORD"); + if (!vsGlobalKeyword) { + if (this->GlobalGenerator->TargetsAndroid()) { + e1.Element("Keyword", "Android"); + } else { + e1.Element("Keyword", "Win32Proj"); + } + } else { + e1.Element("Keyword", *vsGlobalKeyword); + } + + cmValue vsGlobalRootNamespace = + this->GeneratorTarget->GetProperty("VS_GLOBAL_ROOTNAMESPACE"); + if (vsGlobalRootNamespace) { + e1.Element("RootNamespace", *vsGlobalRootNamespace); + } + + std::vector keys = this->GeneratorTarget->GetPropertyKeys(); + for (std::string const& keyIt : keys) { + static const cm::string_view prefix = "VS_GLOBAL_"; + if (!cmHasPrefix(keyIt, prefix)) + continue; + cm::string_view globalKey = cm::string_view(keyIt).substr(prefix.length()); + // Skip invalid or separately-handled properties. + if (globalKey.empty() || globalKey == "PROJECT_TYPES" || + globalKey == "ROOTNAMESPACE" || globalKey == "KEYWORD") { + continue; + } + cmValue value = this->GeneratorTarget->GetProperty(keyIt); + if (!value) + continue; + e1.Element(globalKey, *value); + } } void cmVisualStudio10TargetGenerator::WritePackageReferences(Elem& e0) diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index ec6362f..83e8ee1 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -18,6 +18,7 @@ class cmComputeLinkInformation; class cmCustomCommand; class cmCustomCommandGenerator; +class cmGeneratedFileStream; class cmGlobalVisualStudio10Generator; class cmLocalVisualStudio10Generator; class cmMakefile; @@ -260,6 +261,12 @@ private: void ClassifyAllConfigSources(); void ClassifyAllConfigSource(cmGeneratorTarget::AllConfigSource const& acs); + // .Net SDK-stype project variable and helper functions + void WriteClassicMsBuildProjectFile(cmGeneratedFileStream& BuildFileStream); + + void WriteCommonPropertyGroupGlobals( + cmVisualStudio10TargetGenerator::Elem& e1); + std::unordered_map ParsedToolTargetSettings; bool PropertyIsSameInAllConfigs(const ConfigToSettings& toolSettings, const std::string& propName); -- cgit v0.12 From a450cc9533ac8a54d14ec5e0cbf6b379a2014e7a Mon Sep 17 00:00:00 2001 From: Sumit Bhardwaj Date: Wed, 15 Dec 2021 08:10:57 -0800 Subject: VS: Set ResolveNugetPackages to false for ALL_BUILD and ZERO_CHECK When SDK-style targets in a project are restored, ResolveNugetPackageAssets target is not skipped. However, ALL_BUILD and ZERO_CHECK do not have any nuget packages to resolve and the build fails. This commit sets ResolveNugetPackages to false which skips the target and the build succeeds. --- Source/cmVisualStudio10TargetGenerator.cxx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 088369c..6f6b0a6 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -461,6 +461,20 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile( e1.Element("PreferredToolArchitecture", hostArch); } + // ALL_BUILD and ZERO_CHECK projects transitively include + // Microsoft.Common.CurrentVersion.targets which triggers Target + // ResolveNugetPackageAssets when SDK-style targets are in the project. + // However, these projects have no nuget packages to reference and the + // build fails. + // Setting ResolveNugetPackages to false skips this target and the build + // succeeds. + std::string_view targetName{ this->GeneratorTarget->Target->GetName() }; + if (targetName == "ALL_BUILD" || + targetName == CMAKE_CHECK_BUILD_SYSTEM_TARGET) { + Elem e1(e0, "PropertyGroup"); + e1.Element("ResolveNugetPackages", "false"); + } + if (this->ProjectType != VsProjectType::csproj) { this->WriteProjectConfigurations(e0); } -- cgit v0.12 From 0eea32a376fc54b198fa4690ca57b829a2d14baa Mon Sep 17 00:00:00 2001 From: Sumit Bhardwaj Date: Wed, 15 Dec 2021 08:21:34 -0800 Subject: VS: Add DOTNET_SDK property to generate SDK-style C# projects Changes in cmVisualStudio10TargetGenerator::Generate to write .Net SDK-style project for VS generators VS 19 and above. Also adds documentation and tests. Issue: #20227 --- Auxiliary/vim/syntax/cmake.vim | 2 + Help/manual/cmake-properties.7.rst | 1 + Help/manual/cmake-variables.7.rst | 1 + Help/prop_tgt/DOTNET_SDK.rst | 25 +++++ Help/release/dev/vs-csharp-dotnet-sdk.rst | 9 ++ Help/variable/CMAKE_DOTNET_SDK.rst | 9 ++ Source/cmGeneratorTarget.cxx | 5 + Source/cmGeneratorTarget.h | 2 + Source/cmGlobalVisualStudio7Generator.cxx | 10 +- Source/cmTarget.cxx | 4 + Source/cmVisualStudio10TargetGenerator.cxx | 119 ++++++++++++++++++++- Source/cmVisualStudio10TargetGenerator.h | 3 + Tests/RunCMake/CMakeLists.txt | 4 + Tests/RunCMake/VsDotnetSdk/CMakeLists.txt | 3 + .../VsDotnetSdk/DotnetSdkVariables-check.cmake | 52 +++++++++ .../RunCMake/VsDotnetSdk/DotnetSdkVariables.cmake | 14 +++ Tests/RunCMake/VsDotnetSdk/RunCMakeTest.cmake | 17 +++ Tests/RunCMake/VsDotnetSdk/VsDotnetSdk.cmake | 18 ++++ .../VsDotnetSdkCustomCommandsSource-result.txt | 0 .../VsDotnetSdkCustomCommandsSource-stderr.txt | 7 ++ .../VsDotnetSdkCustomCommandsSource.cmake | 15 +++ .../VsDotnetSdkCustomCommandsTarget-result.txt | 0 .../VsDotnetSdkCustomCommandsTarget-stderr.txt | 7 ++ .../VsDotnetSdkCustomCommandsTarget.cmake | 12 +++ Tests/RunCMake/VsDotnetSdk/csharponly.cs | 11 ++ Tests/RunCMake/VsDotnetSdk/lib1.cs | 10 ++ 26 files changed, 357 insertions(+), 3 deletions(-) create mode 100644 Help/prop_tgt/DOTNET_SDK.rst create mode 100644 Help/release/dev/vs-csharp-dotnet-sdk.rst create mode 100644 Help/variable/CMAKE_DOTNET_SDK.rst create mode 100644 Tests/RunCMake/VsDotnetSdk/CMakeLists.txt create mode 100644 Tests/RunCMake/VsDotnetSdk/DotnetSdkVariables-check.cmake create mode 100644 Tests/RunCMake/VsDotnetSdk/DotnetSdkVariables.cmake create mode 100644 Tests/RunCMake/VsDotnetSdk/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdk.cmake create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource-result.txt create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource-stderr.txt create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource.cmake create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget-result.txt create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget-stderr.txt create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget.cmake create mode 100644 Tests/RunCMake/VsDotnetSdk/csharponly.cs create mode 100644 Tests/RunCMake/VsDotnetSdk/lib1.cs diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim index 52330f7..c6c5d23 100644 --- a/Auxiliary/vim/syntax/cmake.vim +++ b/Auxiliary/vim/syntax/cmake.vim @@ -152,6 +152,7 @@ syn keyword cmakeProperty contained \ DISABLED \ DISABLED_FEATURES \ DISABLE_PRECOMPILE_HEADERS + \ DOTNET_SDK \ DOTNET_TARGET_FRAMEWORK \ DOTNET_TARGET_FRAMEWORK_VERSION \ ECLIPSE_EXTRA_CPROJECT_CONTENTS @@ -1001,6 +1002,7 @@ syn keyword cmakeVariable contained \ CMAKE_DIRECTORY_LABELS \ CMAKE_DISABLE_PRECOMPILE_HEADERS \ CMAKE_DL_LIBS + \ CMAKE_DOTNET_SDK \ CMAKE_DOTNET_TARGET_FRAMEWORK \ CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION \ CMAKE_ECLIPSE_GENERATE_LINKED_RESOURCES diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 5e18e10..73e57d1 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -191,6 +191,7 @@ Properties on Targets /prop_tgt/DEPLOYMENT_REMOTE_DIRECTORY /prop_tgt/DEPRECATION /prop_tgt/DISABLE_PRECOMPILE_HEADERS + /prop_tgt/DOTNET_SDK /prop_tgt/DOTNET_TARGET_FRAMEWORK /prop_tgt/DOTNET_TARGET_FRAMEWORK_VERSION /prop_tgt/EchoString diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 3c50117..13704c5 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -49,6 +49,7 @@ Variables that Provide Information /variable/CMAKE_DEBUG_TARGET_PROPERTIES /variable/CMAKE_DIRECTORY_LABELS /variable/CMAKE_DL_LIBS + /variable/CMAKE_DOTNET_SDK /variable/CMAKE_DOTNET_TARGET_FRAMEWORK /variable/CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION /variable/CMAKE_EDIT_COMMAND diff --git a/Help/prop_tgt/DOTNET_SDK.rst b/Help/prop_tgt/DOTNET_SDK.rst new file mode 100644 index 0000000..ca1dcac --- /dev/null +++ b/Help/prop_tgt/DOTNET_SDK.rst @@ -0,0 +1,25 @@ +DOTNET_SDK +---------- + +.. versionadded:: 3.23 + +Specify the .NET SDK for C# projects. For example: ``Microsoft.NET.Sdk``. + +This property tells :ref:`Visual Studio Generators` for VS 2019 and +above to generate a .NET SDK-style project using the specified SDK. +The property is meaningful only to these generators, and only in C# +targets. It is ignored for C++ projects, even if they are managed +(e.g. using :prop_tgt:`COMMON_LANGUAGE_RUNTIME`). + +This property must be a non-empty string to generate .NET SDK-style projects. +CMake does not perform any validations for the value of the property. + +This property may be initialized for all targets using the +:variable:`CMAKE_DOTNET_SDK` variable. + +.. note:: + + The :ref:`Visual Studio Generators` in this version of CMake have not + yet learned to support :command:`add_custom_command` in .NET SDK-style + projects. It is currently an error to attach a custom command to a + target with the ``DOTNET_SDK`` property set. diff --git a/Help/release/dev/vs-csharp-dotnet-sdk.rst b/Help/release/dev/vs-csharp-dotnet-sdk.rst new file mode 100644 index 0000000..cc0ebe4 --- /dev/null +++ b/Help/release/dev/vs-csharp-dotnet-sdk.rst @@ -0,0 +1,9 @@ +vs-csharp-dotnet-sdk +-------------------- + +* The :ref:`Visual Studio Generators` for VS 2019 and above learned to + support .NET SDK-style project files (``.csproj``) for C# projects. + See the :prop_tgt:`DOTNET_SDK` target property and corresponding + :variable:`CMAKE_DOTNET_SDK` variable. + However, this version of CMake does not yet support using + :command:`add_custom_command` in .NET SDK-style projects. diff --git a/Help/variable/CMAKE_DOTNET_SDK.rst b/Help/variable/CMAKE_DOTNET_SDK.rst new file mode 100644 index 0000000..dc8806a --- /dev/null +++ b/Help/variable/CMAKE_DOTNET_SDK.rst @@ -0,0 +1,9 @@ +CMAKE_DOTNET_SDK +---------------- + +.. versionadded:: 3.23 + +Default value for :prop_tgt:`DOTNET_SDK` property of targets. + +This variable is used to initialize the :prop_tgt:`DOTNET_SDK` +property on all targets. See that target property for additional information. diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index e539e1d..c54bd15 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -7719,6 +7719,11 @@ bool cmGeneratorTarget::IsCSharpOnly() const return languages.size() == 1 && languages.count("CSharp") > 0; } +bool cmGeneratorTarget::IsDotNetSdkTarget() const +{ + return !this->GetProperty("DOTNET_SDK").IsEmpty(); +} + void cmGeneratorTarget::ComputeLinkImplementationLanguages( const std::string& config, cmOptionalLinkImplementation& impl) const { diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index eff5253..76458bd 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -431,6 +431,8 @@ public: bool IsCSharpOnly() const; + bool IsDotNetSdkTarget() const; + void GetObjectLibrariesCMP0026( std::vector& objlibs) const; diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index b3f8d90..c72b109 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -373,8 +373,16 @@ void cmGlobalVisualStudio7Generator::WriteTargetConfigurations( this->IsPartOfDefaultBuild(configs, projectTargets, target); cmValue vcprojName = target->GetProperty("GENERATOR_FILE_NAME"); if (vcprojName) { + std::string mapping; + + // On VS 19 and above, always map .NET SDK projects to "Any CPU". + if (target->IsDotNetSdkTarget() && + this->GetVersion() >= VSVersion::VS16 && + !this->IsReservedTarget(target->GetName())) { + mapping = "Any CPU"; + } this->WriteProjectConfigurations(fout, *vcprojName, *target, configs, - configsPartOfDefaultBuild); + configsPartOfDefaultBuild, mapping); } } } diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index ebf5bd0..f384463 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -525,6 +525,10 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->impl->PolicyMap.Set(cmPolicies::CMP0022, cmPolicies::NEW); } + if (!this->IsImported()) { + initProp("DOTNET_SDK"); + } + if (this->impl->TargetType <= cmStateEnums::GLOBAL_TARGET) { initProp("DOTNET_TARGET_FRAMEWORK"); initProp("DOTNET_TARGET_FRAMEWORK_VERSION"); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 6f6b0a6..ddfe24d 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -406,7 +406,14 @@ void cmVisualStudio10TargetGenerator::Generate() char magic[] = { char(0xEF), char(0xBB), char(0xBF) }; BuildFileStream.write(magic, 3); - this->WriteClassicMsBuildProjectFile(BuildFileStream); + if (this->Managed && this->ProjectType == VsProjectType::csproj && + this->GeneratorTarget->IsDotNetSdkTarget() && + this->GlobalGenerator->GetVersion() >= + cmGlobalVisualStudioGenerator::VS16) { + this->WriteSdkStyleProjectFile(BuildFileStream); + } else { + this->WriteClassicMsBuildProjectFile(BuildFileStream); + } if (BuildFileStream.Close()) { this->GlobalGenerator->FileReplacedDuringGenerate(PathToProjectFile); @@ -468,7 +475,7 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile( // build fails. // Setting ResolveNugetPackages to false skips this target and the build // succeeds. - std::string_view targetName{ this->GeneratorTarget->Target->GetName() }; + cm::string_view targetName{ this->GeneratorTarget->GetName() }; if (targetName == "ALL_BUILD" || targetName == CMAKE_CHECK_BUILD_SYSTEM_TARGET) { Elem e1(e0, "PropertyGroup"); @@ -822,6 +829,96 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile( } } +void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile( + cmGeneratedFileStream& BuildFileStream) +{ + if (!this->Managed || this->ProjectType != VsProjectType::csproj || + !this->GeneratorTarget->IsDotNetSdkTarget()) { + std::string message = "The target \"" + this->GeneratorTarget->GetName() + + "\" is not eligible for .Net SDK style project."; + this->Makefile->IssueMessage(MessageType::INTERNAL_ERROR, message); + return; + } + + if (this->HasCustomCommands()) { + std::string message = "The target \"" + this->GeneratorTarget->GetName() + + "\" does not currently support add_custom_command as the Visual Studio " + "generators have not yet learned how to generate custom commands in " + ".Net SDK-style projects."; + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, message); + return; + } + + Elem e0(BuildFileStream, "Project"); + e0.Attribute("Sdk", *this->GeneratorTarget->GetProperty("DOTNET_SDK")); + + { + Elem e1(e0, "PropertyGroup"); + this->WriteCommonPropertyGroupGlobals(e1); + + e1.Element("EnableDefaultItems", "false"); + // Disable the project upgrade prompt that is displayed the first time a + // project using an older toolset version is opened in a newer version + // of the IDE. + e1.Element("VCProjectUpgraderObjectName", "NoUpgrade"); + e1.Element("ManagedAssembly", "true"); + + cmValue targetFramework = + this->GeneratorTarget->GetProperty("DOTNET_TARGET_FRAMEWORK"); + if (targetFramework) { + if (targetFramework->find(';') != std::string::npos) { + e1.Element("TargetFrameworks", *targetFramework); + } else { + e1.Element("TargetFramework", *targetFramework); + } + } else { + e1.Element("TargetFramework", "net5.0"); + } + + std::string outputType; + switch (this->GeneratorTarget->GetType()) { + case cmStateEnums::OBJECT_LIBRARY: + case cmStateEnums::STATIC_LIBRARY: + case cmStateEnums::MODULE_LIBRARY: + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Target \"", this->GeneratorTarget->GetName(), + "\" is of a type not supported for managed binaries.")); + return; + case cmStateEnums::SHARED_LIBRARY: + outputType = "Library"; + break; + case cmStateEnums::EXECUTABLE: { + auto const win32 = + this->GeneratorTarget->GetSafeProperty("WIN32_EXECUTABLE"); + if (win32.find("$<") != std::string::npos) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Target \"", this->GeneratorTarget->GetName(), + "\" has a generator expression in its WIN32_EXECUTABLE " + "property. This is not supported on managed " + "executables.")); + return; + } + outputType = "Exe"; + } break; + case cmStateEnums::UTILITY: + case cmStateEnums::INTERFACE_LIBRARY: + case cmStateEnums::GLOBAL_TARGET: + outputType = "Utility"; + break; + case cmStateEnums::UNKNOWN_LIBRARY: + break; + } + e1.Element("OutputType", outputType); + } + + this->WriteDotNetDocumentationFile(e0); + this->WriteAllSources(e0); + this->WritePackageReferences(e0); + this->WriteProjectReferences(e0); +} + void cmVisualStudio10TargetGenerator::WriteCommonPropertyGroupGlobals(Elem& e1) { e1.Attribute("Label", "Globals"); @@ -873,6 +970,24 @@ void cmVisualStudio10TargetGenerator::WriteCommonPropertyGroupGlobals(Elem& e1) } } +bool cmVisualStudio10TargetGenerator::HasCustomCommands() const +{ + if (!this->GeneratorTarget->GetPreBuildCommands().empty() || + !this->GeneratorTarget->GetPreLinkCommands().empty() || + !this->GeneratorTarget->GetPostBuildCommands().empty()) { + return true; + } + + for (cmGeneratorTarget::AllConfigSource const& si : + this->GeneratorTarget->GetAllConfigSources()) { + if (si.Source->GetCustomCommand()) { + return true; + } + } + + return false; +} + void cmVisualStudio10TargetGenerator::WritePackageReferences(Elem& e0) { std::vector packageReferences; diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 83e8ee1..37b8dfd 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -263,10 +263,13 @@ private: // .Net SDK-stype project variable and helper functions void WriteClassicMsBuildProjectFile(cmGeneratedFileStream& BuildFileStream); + void WriteSdkStyleProjectFile(cmGeneratedFileStream& BuildFileStream); void WriteCommonPropertyGroupGlobals( cmVisualStudio10TargetGenerator::Elem& e1); + bool HasCustomCommands() const; + std::unordered_map ParsedToolTargetSettings; bool PropertyIsSameInAllConfigs(const ConfigToSettings& toolSettings, const std::string& propName); diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 5dc7031..dec147b 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -616,6 +616,10 @@ if("${CMAKE_GENERATOR}" MATCHES "Visual Studio ([^9]|9[0-9])") endif() endif() +if(CMAKE_GENERATOR MATCHES "^Visual Studio (1[6-9]|[2-9][0-9])") + add_RunCMake_test(VsDotnetSdk) +endif() + if(XCODE_VERSION) add_RunCMake_test(XcodeProject -DXCODE_VERSION=${XCODE_VERSION}) add_RunCMake_test(XcodeProject-Embed -DXCODE_VERSION=${XCODE_VERSION}) diff --git a/Tests/RunCMake/VsDotnetSdk/CMakeLists.txt b/Tests/RunCMake/VsDotnetSdk/CMakeLists.txt new file mode 100644 index 0000000..e597708 --- /dev/null +++ b/Tests/RunCMake/VsDotnetSdk/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.22.0) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/VsDotnetSdk/DotnetSdkVariables-check.cmake b/Tests/RunCMake/VsDotnetSdk/DotnetSdkVariables-check.cmake new file mode 100644 index 0000000..7a5cd1d --- /dev/null +++ b/Tests/RunCMake/VsDotnetSdk/DotnetSdkVariables-check.cmake @@ -0,0 +1,52 @@ +set(files foo.csproj bar.csproj baz.csproj) + +set(inLib1 FALSE) +set(dotnetSdkInLib1 FALSE) + +set(inLib2 FALSE) +set(dotnetSdkWebInLib2 FALSE) + +set(inLib3 FALSE) +set(classicProjInLib3 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 "") + set(dotnetSdkInLib1 TRUE) + set(inLib1 TRUE) + endif() + elseif(NOT inLib2) + if(line MATCHES "") + set(dotnetSdkWebInLib2 TRUE) + set(inLib2 TRUE) + endif() + elseif(NOT inLib3) + if(line MATCHES "