diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index d1e6ad9477..217306b1d8 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -935,8 +935,6 @@ if(WIN32) cmGlobalNMakeMakefileGenerator.h cmGlobalJOMMakefileGenerator.cxx cmGlobalJOMMakefileGenerator.h - cmGlobalVisualStudio71Generator.cxx - cmGlobalVisualStudio71Generator.h cmGlobalVisualStudio7Generator.cxx cmGlobalVisualStudio7Generator.h cmGlobalVisualStudio8Generator.cxx @@ -975,6 +973,9 @@ if(WIN32) cmVisualStudioWCEPlatformParser.cxx cmVSSetupHelper.cxx cmVSSetupHelper.h + cmVSSolution.cxx + cmVSSolution.h + cmVSVersion.h ) # Add a manifest file to executables on Windows to allow for diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index d395599d63..a657881210 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -3497,10 +3497,11 @@ void cmGlobalGenerator::GetFilesReplacedDuringGenerate( std::back_inserter(filenames)); } -void cmGlobalGenerator::GetTargetSets( - TargetDependSet& projectTargets, TargetDependSet& originalTargets, - cmLocalGenerator* root, std::vector& generators) +cmGlobalGenerator::TargetDependSet cmGlobalGenerator::GetTargetsForProject( + cmLocalGenerator const* root, + std::vector const& generators) const { + TargetDependSet projectTargets; // loop over all local generators for (auto* generator : generators) { // check to make sure generator is not excluded @@ -3513,12 +3514,11 @@ void cmGlobalGenerator::GetTargetSets( target->GetLocalGenerator() != root) { continue; } - // put the target in the set of original targets - originalTargets.insert(target.get()); // Get the set of targets that depend on target this->AddTargetDepends(target.get(), projectTargets); } } + return projectTargets; } bool cmGlobalGenerator::IsRootOnlyTarget(cmGeneratorTarget* target) const @@ -3528,7 +3528,7 @@ bool cmGlobalGenerator::IsRootOnlyTarget(cmGeneratorTarget* target) const } void cmGlobalGenerator::AddTargetDepends(cmGeneratorTarget const* target, - TargetDependSet& projectTargets) + TargetDependSet& projectTargets) const { // add the target itself if (projectTargets.insert(target).second) { diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 38091de0c2..1589d2e8ca 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -704,14 +704,15 @@ public: cmXcFrameworkPlist const& content); protected: - // for a project collect all its targets by following depend - // information, and also collect all the targets - void GetTargetSets(TargetDependSet& projectTargets, - TargetDependSet& originalTargets, cmLocalGenerator* root, - std::vector& generators); + /** Get all targets produced under the given root, plus the transitive + closure of targets on which they depend, possibly from other dirs. */ + TargetDependSet GetTargetsForProject( + cmLocalGenerator const* root, + std::vector const& generators) const; + bool IsRootOnlyTarget(cmGeneratorTarget* target) const; void AddTargetDepends(cmGeneratorTarget const* target, - TargetDependSet& projectTargets); + TargetDependSet& projectTargets) const; void SetLanguageEnabledFlag(std::string const& l, cmMakefile* mf); void SetLanguageEnabledMaps(std::string const& l, cmMakefile* mf); void FillExtensionToLanguageMap(std::string const& l, cmMakefile* mf); diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx index 94871537fa..8b0b4e13c8 100644 --- a/Source/cmGlobalGhsMultiGenerator.cxx +++ b/Source/cmGlobalGhsMultiGenerator.cxx @@ -439,9 +439,8 @@ void cmGlobalGhsMultiGenerator::OutputTopLevelProject( // Collect all targets under this root generator and the transitive // closure of their dependencies. - TargetDependSet projectTargets; - TargetDependSet originalTargets; - this->GetTargetSets(projectTargets, originalTargets, root, generators); + TargetDependSet const projectTargets = + this->GetTargetsForProject(root, generators); OrderedTargetDependSet sortedProjectTargets(projectTargets, ""); this->ProjectTargets.clear(); for (cmGeneratorTarget const* t : sortedProjectTargets) { diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index fe773af963..b775d7189c 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -23,7 +23,6 @@ #include "cmExperimental.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" -#include "cmGlobalVisualStudio71Generator.h" #include "cmGlobalVisualStudio7Generator.h" #include "cmGlobalVisualStudioGenerator.h" #include "cmIDEFlagTable.h" @@ -870,7 +869,7 @@ std::string cmGlobalVisualStudio10Generator::FindDevEnvCommand() // Skip over the cmGlobalVisualStudio8Generator implementation because // we expect a real devenv and do not want to look for VCExpress. // NOLINTNEXTLINE(bugprone-parent-virtual-call) - return this->cmGlobalVisualStudio71Generator::FindDevEnvCommand(); + return this->cmGlobalVisualStudio7Generator::FindDevEnvCommand(); } bool cmGlobalVisualStudio10Generator::FindVCTargetsPath(cmMakefile* mf) diff --git a/Source/cmGlobalVisualStudio11Generator.cxx b/Source/cmGlobalVisualStudio11Generator.cxx index b961ae5875..89c4fe6d7e 100644 --- a/Source/cmGlobalVisualStudio11Generator.cxx +++ b/Source/cmGlobalVisualStudio11Generator.cxx @@ -103,15 +103,6 @@ bool cmGlobalVisualStudio11Generator::SelectWindowsStoreToolset( toolset); } -bool cmGlobalVisualStudio11Generator::UseFolderProperty() const -{ - // Intentionally skip up to the top-level class implementation. - // Folders are not supported by the Express editions in VS10 and earlier, - // but they are in VS11 Express and above. - // NOLINTNEXTLINE(bugprone-parent-virtual-call) - return cmGlobalGenerator::UseFolderProperty(); -} - std::set cmGlobalVisualStudio11Generator::GetInstalledWindowsCESDKs() { diff --git a/Source/cmGlobalVisualStudio11Generator.h b/Source/cmGlobalVisualStudio11Generator.h index 4b00fa26a5..fe348fb846 100644 --- a/Source/cmGlobalVisualStudio11Generator.h +++ b/Source/cmGlobalVisualStudio11Generator.h @@ -47,7 +47,6 @@ protected: bool IsWindowsPhoneToolsetInstalled() const; bool IsWindowsStoreToolsetInstalled() const; - bool UseFolderProperty() const override; static std::set GetInstalledWindowsCESDKs(); /** Return true if target system supports debugging deployment. */ diff --git a/Source/cmGlobalVisualStudio14Generator.cxx b/Source/cmGlobalVisualStudio14Generator.cxx index 5ba394f81b..e37ed0be74 100644 --- a/Source/cmGlobalVisualStudio14Generator.cxx +++ b/Source/cmGlobalVisualStudio14Generator.cxx @@ -521,60 +521,3 @@ std::string cmGlobalVisualStudio14Generator::GetWindows10SDKVersion( // Return an empty string return std::string(); } - -void cmGlobalVisualStudio14Generator::AddSolutionItems(cmLocalGenerator* root, - VSFolders& vsFolders) -{ - cmValue n = root->GetMakefile()->GetProperty("VS_SOLUTION_ITEMS"); - if (cmNonempty(n)) { - cmMakefile* makefile = root->GetMakefile(); - - std::vector sourceGroups = makefile->GetSourceGroups(); - - cmVisualStudioFolder* defaultFolder = nullptr; - - std::vector pathComponents = { - makefile->GetCurrentSourceDirectory(), - "", - "", - }; - - for (std::string const& relativePath : cmList(n)) { - pathComponents[2] = relativePath; - - std::string fullPath = cmSystemTools::FileIsFullPath(relativePath) - ? relativePath - : cmSystemTools::JoinPath(pathComponents); - - cmSourceGroup* sg = makefile->FindSourceGroup(fullPath, sourceGroups); - - cmVisualStudioFolder* folder = nullptr; - if (!sg->GetFullName().empty()) { - std::string folderPath = sg->GetFullName(); - // Source groups use '\' while solution folders use '/'. - cmSystemTools::ReplaceString(folderPath, "\\", "/"); - folder = vsFolders.Create(folderPath); - } else { - // Lazily initialize the default solution items folder. - if (defaultFolder == nullptr) { - defaultFolder = vsFolders.Create("Solution Items"); - } - folder = defaultFolder; - } - - folder->SolutionItems.insert(fullPath); - } - } -} - -void cmGlobalVisualStudio14Generator::WriteFolderSolutionItems( - std::ostream& fout, cmVisualStudioFolder const& folder) const -{ - fout << "\tProjectSection(SolutionItems) = preProject\n"; - - for (std::string const& item : folder.SolutionItems) { - fout << "\t\t" << item << " = " << item << "\n"; - } - - fout << "\tEndProjectSection\n"; -} diff --git a/Source/cmGlobalVisualStudio14Generator.h b/Source/cmGlobalVisualStudio14Generator.h index a71326f934..d9d928a73f 100644 --- a/Source/cmGlobalVisualStudio14Generator.h +++ b/Source/cmGlobalVisualStudio14Generator.h @@ -66,11 +66,6 @@ protected: std::string GetWindows10SDKVersion(cmMakefile* mf); - void AddSolutionItems(cmLocalGenerator* root, VSFolders& vsFolders) override; - - void WriteFolderSolutionItems( - std::ostream& fout, cmVisualStudioFolder const& folder) const override; - private: class Factory; friend class Factory; diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx deleted file mode 100644 index 26ff741b0f..0000000000 --- a/Source/cmGlobalVisualStudio71Generator.cxx +++ /dev/null @@ -1,168 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file LICENSE.rst or https://cmake.org/licensing for details. */ -#include "cmGlobalVisualStudio71Generator.h" - -#include -#include - -#include "cmGeneratorTarget.h" -#include "cmGlobalGenerator.h" -#include "cmGlobalVisualStudioGenerator.h" -#include "cmList.h" -#include "cmListFileCache.h" -#include "cmLocalGenerator.h" -#include "cmMakefile.h" -#include "cmStringAlgorithms.h" -#include "cmSystemTools.h" - -class cmake; - -cmGlobalVisualStudio71Generator::cmGlobalVisualStudio71Generator(cmake* cm) - : cmGlobalVisualStudio7Generator(cm) -{ - this->ProjectConfigurationSectionName = "ProjectConfiguration"; -} - -void cmGlobalVisualStudio71Generator::WriteSLNFile( - std::ostream& fout, cmLocalGenerator* root, - OrderedTargetDependSet const& orderedProjectTargets, - VSFolders const& vsFolders) const -{ - std::vector configs = - root->GetMakefile()->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); - - // Write out the header for a SLN file - this->WriteSLNHeader(fout); - - // Generate folder specification. - if (!vsFolders.Folders.empty()) { - this->WriteFolders(fout, vsFolders); - } - - // Now write the actual target specification content. - this->WriteTargetsToSolution(fout, root, orderedProjectTargets); - - // Write out the configurations information for the solution - fout << "Global\n"; - // Write out the configurations for the solution - this->WriteSolutionConfigurations(fout, configs); - fout << "\tGlobalSection(" << this->ProjectConfigurationSectionName - << ") = postSolution\n"; - // Write out the configurations for all the targets in the project - this->WriteTargetConfigurations(fout, configs, orderedProjectTargets); - fout << "\tEndGlobalSection\n"; - - if (!vsFolders.Folders.empty()) { - // Write out project folders - fout << "\tGlobalSection(NestedProjects) = preSolution\n"; - this->WriteFoldersContent(fout, vsFolders); - fout << "\tEndGlobalSection\n"; - } - - // Write out global sections - this->WriteSLNGlobalSections(fout, root); - - // Write the footer for the SLN file - this->WriteSLNFooter(fout); -} - -// Write a dsp file into the SLN file, -// Note, that dependencies from executables to -// the libraries it uses are also done here -void cmGlobalVisualStudio71Generator::WriteProject( - std::ostream& fout, std::string const& dspname, std::string const& dir, - cmGeneratorTarget const* t) const -{ - // check to see if this is a fortran build - std::string ext = ".vcproj"; - char const* project = - R"(Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = ")"; - if (this->TargetIsFortranOnly(t)) { - ext = ".vfproj"; - project = R"(Project("{6989167D-11E4-40FE-8C1A-2192A86A7E90}") = ")"; - } - if (t->IsCSharpOnly()) { - ext = ".csproj"; - project = R"(Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = ")"; - } - cmValue targetExt = t->GetProperty("GENERATOR_FILE_NAME_EXT"); - if (targetExt) { - ext = *targetExt; - } - - std::string guid = this->GetGUID(dspname); - fout << project << dspname << "\", \"" << this->ConvertToSolutionPath(dir) - << (!dir.empty() ? "\\" : "") << dspname << ext << "\", \"{" << guid - << "}\"\n"; - fout << "\tProjectSection(ProjectDependencies) = postProject\n"; - this->WriteProjectDepends(fout, dspname, dir, t); - fout << "\tEndProjectSection\n"; - - fout << "EndProject\n"; -} - -// Write a dsp file into the SLN file, Note, that dependencies from -// executables to the libraries it uses are also done here -void cmGlobalVisualStudio71Generator::WriteExternalProject( - std::ostream& fout, std::string const& name, std::string const& location, - cmValue typeGuid, - std::set>> const& depends) const -{ - fout << "Project(\"{" - << (typeGuid ? *typeGuid - : std::string( - cmGlobalVisualStudio71Generator::ExternalProjectType( - location))) - << "}\") = \"" << name << "\", \"" - << this->ConvertToSolutionPath(location) << "\", \"{" - << this->GetGUID(name) << "}\"\n"; - - // write out the dependencies here VS 7.1 includes dependencies with the - // project instead of in the global section - if (!depends.empty()) { - fout << "\tProjectSection(ProjectDependencies) = postProject\n"; - for (BT> const& it : depends) { - std::string const& dep = it.Value.first; - if (this->IsDepInSolution(dep)) { - fout << "\t\t{" << this->GetGUID(dep) << "} = {" << this->GetGUID(dep) - << "}\n"; - } - } - fout << "\tEndProjectSection\n"; - } - - fout << "EndProject\n"; -} - -// Write a dsp file into the SLN file, Note, that dependencies from -// executables to the libraries it uses are also done here -void cmGlobalVisualStudio71Generator::WriteProjectConfigurations( - std::ostream& fout, std::string const& name, cmGeneratorTarget const& target, - std::vector const& configs, - std::set const& configsPartOfDefaultBuild, - std::string const& platformMapping) const -{ - std::string const& platformName = - !platformMapping.empty() ? platformMapping : this->GetPlatformName(); - std::string guid = this->GetGUID(name); - for (std::string const& i : configs) { - cmList mapConfig; - char const* dstConfig = i.c_str(); - if (target.GetProperty("EXTERNAL_MSPROJECT")) { - if (cmValue m = target.GetProperty( - cmStrCat("MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(i)))) { - mapConfig.assign(*m); - if (!mapConfig.empty()) { - dstConfig = mapConfig[0].c_str(); - } - } - } - fout << "\t\t{" << guid << "}." << i << ".ActiveCfg = " << dstConfig << '|' - << platformName << std::endl; - auto ci = configsPartOfDefaultBuild.find(i); - if (!(ci == configsPartOfDefaultBuild.end())) { - fout << "\t\t{" << guid << "}." << i << ".Build.0 = " << dstConfig << '|' - << platformName << std::endl; - } - } -} diff --git a/Source/cmGlobalVisualStudio71Generator.h b/Source/cmGlobalVisualStudio71Generator.h deleted file mode 100644 index 70fa64cba2..0000000000 --- a/Source/cmGlobalVisualStudio71Generator.h +++ /dev/null @@ -1,53 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file LICENSE.rst or https://cmake.org/licensing for details. */ -#pragma once - -#include -#include -#include -#include -#include - -#include "cmGlobalVisualStudio7Generator.h" -#include "cmValue.h" - -class cmGeneratorTarget; -class cmLocalGenerator; -class cmake; -template -class BT; - -/** \class cmGlobalVisualStudio71Generator - * \brief Write a Unix makefiles. - * - * cmGlobalVisualStudio71Generator manages UNIX build process for a tree - */ -class cmGlobalVisualStudio71Generator : public cmGlobalVisualStudio7Generator -{ -public: - cmGlobalVisualStudio71Generator(cmake* cm); - -protected: - void WriteSLNFile(std::ostream& fout, cmLocalGenerator* root, - OrderedTargetDependSet const& orderedProjectTargets, - VSFolders const& vsFolders) const override; - virtual void WriteSolutionConfigurations( - std::ostream& fout, std::vector const& configs) const = 0; - void WriteProject(std::ostream& fout, std::string const& name, - std::string const& path, - cmGeneratorTarget const* t) const override; - void WriteProjectConfigurations( - std::ostream& fout, std::string const& name, - cmGeneratorTarget const& target, std::vector const& configs, - std::set const& configsPartOfDefaultBuild, - std::string const& platformMapping = "") const override; - void WriteExternalProject( - std::ostream& fout, std::string const& name, std::string const& path, - cmValue typeGuid, - std::set>> const& depends) const override; - - // Folders are not supported by VS 7.1. - bool UseFolderProperty() const override { return false; } - - std::string ProjectConfigurationSectionName; -}; diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index 3e8dfbe72e..646bc1390e 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -28,7 +28,6 @@ #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetDepend.h" -#include "cmUuid.h" #include "cmVisualStudioGeneratorOptions.h" #include "cmake.h" @@ -58,14 +57,6 @@ static cmVS7FlagTable cmVS7ExtraFlagTable[] = { { "", "", "", "", 0 } }; -namespace { -std::string GetSLNFile(cmLocalGenerator* root) -{ - return cmStrCat(root->GetCurrentBinaryDirectory(), '/', - root->GetProjectName(), ".sln"); -} -} - cmGlobalVisualStudio7Generator::cmGlobalVisualStudio7Generator(cmake* cm) : cmGlobalVisualStudioGenerator(cm) { @@ -294,355 +285,6 @@ bool cmGlobalVisualStudio7Generator::SetSystemName(std::string const& s, return this->cmGlobalVisualStudioGenerator::SetSystemName(s, mf); } -void cmGlobalVisualStudio7Generator::Generate() -{ - // first do the superclass method - this->cmGlobalVisualStudioGenerator::Generate(); - - // Now write out the VS Solution files. - for (auto& it : this->ProjectMap) { - this->OutputSLNFile(it.second[0], it.second); - } - - // If any solution or project files changed during the generation, - // tell Visual Studio to reload them... - if (!cmSystemTools::GetErrorOccurredFlag() && - !this->LocalGenerators.empty()) { - this->CallVisualStudioMacro(MacroReload, - GetSLNFile(this->LocalGenerators[0].get())); - } - - if (this->Version == VSVersion::VS14 && - !this->CMakeInstance->GetIsInTryCompile()) { - std::string cmakeWarnVS14; - if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue( - "CMAKE_WARN_VS14")) { - this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS14"); - cmakeWarnVS14 = *cached; - } else { - cmSystemTools::GetEnv("CMAKE_WARN_VS14", cmakeWarnVS14); - } - if (cmakeWarnVS14.empty() || !cmIsOff(cmakeWarnVS14)) { - this->CMakeInstance->IssueMessage( - MessageType::WARNING, - "The \"Visual Studio 14 2015\" generator is deprecated " - "and will be removed in a future version of CMake." - "\n" - "Add CMAKE_WARN_VS14=OFF to the cache to disable this warning."); - } - } -} - -void cmGlobalVisualStudio7Generator::OutputSLNFile( - cmLocalGenerator* root, std::vector& generators) -{ - if (generators.empty()) { - return; - } - - // Collect all targets under this root generator and the transitive - // closure of their dependencies. - TargetDependSet projectTargets; - TargetDependSet originalTargets; - this->GetTargetSets(projectTargets, originalTargets, root, generators); - OrderedTargetDependSet orderedProjectTargets( - projectTargets, this->GetStartupProjectName(root)); - - VSFolders vsFolders = this->CreateSolutionFolders(orderedProjectTargets); - this->AddSolutionItems(root, vsFolders); - - std::string fname = GetSLNFile(root); - cmGeneratedFileStream fout(fname); - fout.SetCopyIfDifferent(true); - if (!fout) { - return; - } - this->WriteSLNFile(fout, root, orderedProjectTargets, vsFolders); - if (fout.Close()) { - this->FileReplacedDuringGenerate(fname); - } -} - -void cmGlobalVisualStudio7Generator::WriteTargetConfigurations( - std::ostream& fout, std::vector const& configs, - OrderedTargetDependSet const& projectTargets) const -{ - // loop over again and write out configurations for each target - // in the solution - for (cmGeneratorTarget const* target : projectTargets) { - if (!this->IsInSolution(target)) { - continue; - } - cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT"); - if (expath) { - std::set allConfigurations(configs.begin(), configs.end()); - cmValue mapping = target->GetProperty("VS_PLATFORM_MAPPING"); - this->WriteProjectConfigurations(fout, target->GetName(), *target, - configs, allConfigurations, - mapping ? *mapping : ""); - } else { - std::set const& configsPartOfDefaultBuild = - 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->Version >= VSVersion::VS16 && - !cmGlobalVisualStudio7Generator::IsReservedTarget( - target->GetName())) { - mapping = "Any CPU"; - } - this->WriteProjectConfigurations(fout, *vcprojName, *target, configs, - configsPartOfDefaultBuild, mapping); - } - } - } -} - -cmGlobalVisualStudio7Generator::VSFolders -cmGlobalVisualStudio7Generator::CreateSolutionFolders( - OrderedTargetDependSet const& orderedProjectTargets) -{ - VSFolders vsFolders; - if (!this->UseFolderProperty()) { - return vsFolders; - } - for (cmGeneratorTarget const* target : orderedProjectTargets) { - if (this->IsInSolution(target) && - (target->GetProperty("EXTERNAL_MSPROJECT") || - target->GetProperty("GENERATOR_FILE_NAME"))) { - // Create "solution folder" information from FOLDER target property - if (cmVisualStudioFolder* folder = - vsFolders.Create(target->GetEffectiveFolderName())) { - folder->Projects.insert(target->GetName()); - } - } - } - return vsFolders; -} - -cmVisualStudioFolder* cmGlobalVisualStudio7Generator::VSFolders::Create( - std::string const& path) -{ - if (path.empty()) { - return nullptr; - } - - std::vector tokens = - cmSystemTools::SplitString(path, '/', false); - - std::string cumulativePath; - - for (std::string const& iter : tokens) { - if (iter.empty()) { - continue; - } - - if (cumulativePath.empty()) { - cumulativePath = cmStrCat("CMAKE_FOLDER_GUID_", iter); - } else { - this->Folders[cumulativePath].Projects.insert( - cmStrCat(cumulativePath, '/', iter)); - - cumulativePath = cmStrCat(cumulativePath, '/', iter); - } - } - - if (cumulativePath.empty()) { - return nullptr; - } - - return &this->Folders[cumulativePath]; -} - -void cmGlobalVisualStudio7Generator::WriteTargetsToSolution( - std::ostream& fout, cmLocalGenerator* root, - OrderedTargetDependSet const& projectTargets) const -{ - std::vector configs = - root->GetMakefile()->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); - - for (cmGeneratorTarget const* target : projectTargets) { - if (!this->IsInSolution(target)) { - continue; - } - // handle external vc project files - cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT"); - if (expath) { - std::string project = target->GetName(); - std::string const& location = *expath; - - this->WriteExternalProject(fout, project, location, - target->GetProperty("VS_PROJECT_TYPE"), - target->GetUtilities()); - } else { - cmValue vcprojName = target->GetProperty("GENERATOR_FILE_NAME"); - if (vcprojName) { - cmLocalGenerator* lg = target->GetLocalGenerator(); - std::string dir = lg->GetCurrentBinaryDirectory(); - dir = root->MaybeRelativeToCurBinDir(dir); - if (dir == "."_s) { - dir.clear(); // msbuild cannot handle ".\" prefix - } - this->WriteProject(fout, *vcprojName, dir, target); - } - } - } -} - -void cmGlobalVisualStudio7Generator::WriteFolders( - std::ostream& fout, VSFolders const& vsFolders) const -{ - cm::string_view const prefix = "CMAKE_FOLDER_GUID_"; - std::string guidProjectTypeFolder = "2150E333-8FDC-42A3-9474-1A3956D46DE8"; - for (auto const& iter : vsFolders.Folders) { - std::string fullName = iter.first; - std::string guid = this->GetGUID(fullName); - - std::replace(fullName.begin(), fullName.end(), '/', '\\'); - if (cmHasPrefix(fullName, prefix)) { - fullName = fullName.substr(prefix.size()); - } - - std::string nameOnly = cmSystemTools::GetFilenameName(fullName); - - fout << "Project(\"{" << guidProjectTypeFolder << "}\") = \"" << nameOnly - << "\", \"" << fullName << "\", \"{" << guid << "}\"\n"; - - if (!iter.second.SolutionItems.empty()) { - this->WriteFolderSolutionItems(fout, iter.second); - } - - fout << "EndProject\n"; - } -} - -void cmGlobalVisualStudio7Generator::WriteFoldersContent( - std::ostream& fout, VSFolders const& vsFolders) const -{ - for (auto const& iter : vsFolders.Folders) { - std::string key(iter.first); - std::string guidParent(this->GetGUID(key)); - - for (std::string const& it : iter.second.Projects) { - std::string const& value(it); - std::string guid(this->GetGUID(value)); - - fout << "\t\t{" << guid << "} = {" << guidParent << "}\n"; - } - } -} - -std::string cmGlobalVisualStudio7Generator::ConvertToSolutionPath( - std::string const& path) const -{ - // Convert to backslashes. Do not use ConvertToOutputPath because - // we will add quoting ourselves, and we know these projects always - // use windows slashes. - std::string d = path; - std::string::size_type pos = 0; - while ((pos = d.find('/', pos)) != std::string::npos) { - d[pos++] = '\\'; - } - return d; -} - -void cmGlobalVisualStudio7Generator::WriteSLNGlobalSections( - std::ostream& fout, cmLocalGenerator* root) const -{ - std::string const guid = - this->GetGUID(cmStrCat(root->GetProjectName(), ".sln")); - bool extensibilityGlobalsOverridden = false; - bool extensibilityAddInsOverridden = false; - std::vector const propKeys = - root->GetMakefile()->GetPropertyKeys(); - for (std::string const& it : propKeys) { - if (cmHasLiteralPrefix(it, "VS_GLOBAL_SECTION_")) { - std::string sectionType; - std::string name = it.substr(18); - if (cmHasLiteralPrefix(name, "PRE_")) { - name = name.substr(4); - sectionType = "preSolution"; - } else if (cmHasLiteralPrefix(name, "POST_")) { - name = name.substr(5); - sectionType = "postSolution"; - } else { - continue; - } - if (!name.empty()) { - bool addGuid = false; - if (name == "ExtensibilityGlobals"_s && - sectionType == "postSolution"_s) { - addGuid = true; - extensibilityGlobalsOverridden = true; - } else if (name == "ExtensibilityAddIns"_s && - sectionType == "postSolution"_s) { - extensibilityAddInsOverridden = true; - } - fout << "\tGlobalSection(" << name << ") = " << sectionType << '\n'; - cmValue p = root->GetMakefile()->GetProperty(it); - cmList keyValuePairs{ *p }; - for (std::string const& itPair : keyValuePairs) { - std::string::size_type const posEqual = itPair.find('='); - if (posEqual != std::string::npos) { - std::string const key = - cmTrimWhitespace(itPair.substr(0, posEqual)); - std::string const value = - cmTrimWhitespace(itPair.substr(posEqual + 1)); - fout << "\t\t" << key << " = " << value << '\n'; - if (key == "SolutionGuid"_s) { - addGuid = false; - } - } - } - if (addGuid) { - fout << "\t\tSolutionGuid = {" << guid << "}\n"; - } - fout << "\tEndGlobalSection\n"; - } - } - } - if (!extensibilityGlobalsOverridden) { - fout << "\tGlobalSection(ExtensibilityGlobals) = postSolution\n" - << "\t\tSolutionGuid = {" << guid << "}\n" - << "\tEndGlobalSection\n"; - } - if (!extensibilityAddInsOverridden) { - fout << "\tGlobalSection(ExtensibilityAddIns) = postSolution\n" - << "\tEndGlobalSection\n"; - } -} - -// Standard end of dsw file -void cmGlobalVisualStudio7Generator::WriteSLNFooter(std::ostream& fout) const -{ - fout << "EndGlobal\n"; -} - -std::string cmGlobalVisualStudio7Generator::GetGUID( - std::string const& name) const -{ - std::string const& guidStoreName = cmStrCat(name, "_GUID_CMAKE"); - if (cmValue storedGUID = - this->CMakeInstance->GetCacheDefinition(guidStoreName)) { - return *storedGUID; - } - // Compute a GUID that is deterministic but unique to the build tree. - std::string input = - cmStrCat(this->CMakeInstance->GetState()->GetBinaryDirectory(), '|', name); - - cmUuid uuidGenerator; - - std::vector uuidNamespace; - uuidGenerator.StringToBinary("ee30c4be-5192-4fb0-b335-722a2dffe760", - uuidNamespace); - - std::string guid = uuidGenerator.FromMd5(uuidNamespace, input); - - return cmSystemTools::UpperCase(guid); -} - void cmGlobalVisualStudio7Generator::AppendDirectoryForConfig( std::string const& prefix, std::string const& config, std::string const& suffix, std::string& dir) @@ -652,63 +294,6 @@ void cmGlobalVisualStudio7Generator::AppendDirectoryForConfig( } } -std::set cmGlobalVisualStudio7Generator::IsPartOfDefaultBuild( - std::vector const& configs, - OrderedTargetDependSet const& projectTargets, - cmGeneratorTarget const* target) const -{ - std::set activeConfigs; - // if it is a utility target then only make it part of the - // default build if another target depends on it - int type = target->GetType(); - if (type == cmStateEnums::GLOBAL_TARGET) { - std::vector targetNames; - targetNames.push_back("INSTALL"); - targetNames.push_back("PACKAGE"); - for (std::string const& t : targetNames) { - // check if target is part of default build - if (target->GetName() == t) { - std::string const propertyName = - cmStrCat("CMAKE_VS_INCLUDE_", t, "_TO_DEFAULT_BUILD"); - // inspect CMAKE_VS_INCLUDE__TO_DEFAULT_BUILD properties - for (std::string const& i : configs) { - cmValue propertyValue = - target->Target->GetMakefile()->GetDefinition(propertyName); - if (propertyValue && - cmIsOn(cmGeneratorExpression::Evaluate( - *propertyValue, target->GetLocalGenerator(), i))) { - activeConfigs.insert(i); - } - } - } - } - return activeConfigs; - } - if (type == cmStateEnums::UTILITY && - !this->IsDependedOn(projectTargets, target)) { - return activeConfigs; - } - // inspect EXCLUDE_FROM_DEFAULT_BUILD[_] properties - for (std::string const& i : configs) { - if (target->GetFeature("EXCLUDE_FROM_DEFAULT_BUILD", i).IsOff()) { - activeConfigs.insert(i); - } - } - return activeConfigs; -} - -bool cmGlobalVisualStudio7Generator::IsDependedOn( - OrderedTargetDependSet const& projectTargets, - cmGeneratorTarget const* gtIn) const -{ - return std::any_of(projectTargets.begin(), projectTargets.end(), - [this, gtIn](cmTargetDepend const& l) { - TargetDependSet const& tgtdeps = - this->GetTargetDirectDepends(l); - return tgtdeps.count(gtIn); - }); -} - std::string cmGlobalVisualStudio7Generator::Encoding() { return "UTF-8"; diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h index a00c540a1d..7cd1ab3ac3 100644 --- a/Source/cmGlobalVisualStudio7Generator.h +++ b/Source/cmGlobalVisualStudio7Generator.h @@ -25,12 +25,6 @@ class cmake; template class BT; -struct cmVisualStudioFolder -{ - std::set Projects; - std::set SolutionItems; -}; - /** \class cmGlobalVisualStudio7Generator * \brief Write a Unix makefiles. * @@ -82,9 +76,6 @@ public: std::vector const& makeOptions = std::vector()) override; - //! Lookup a stored GUID or compute one deterministically. - std::string GetGUID(std::string const& name) const; - /** Append the subdirectory for the given configuration. */ void AppendDirectoryForConfig(std::string const& prefix, std::string const& config, @@ -127,75 +118,11 @@ public: protected: cmGlobalVisualStudio7Generator(cmake* cm); - void Generate() override; - - struct VSFolders - { - std::map Folders; - cmVisualStudioFolder* Create(std::string const& path); - }; - std::string const& GetDevEnvCommand(); virtual std::string FindDevEnvCommand(); static char const* ExternalProjectType(std::string const& location); - virtual void OutputSLNFile(cmLocalGenerator* root, - std::vector& generators); - virtual void WriteSLNFile( - std::ostream& fout, cmLocalGenerator* root, - OrderedTargetDependSet const& orderedProjectTargets, - VSFolders const& vsFolders) const = 0; - virtual void WriteProject(std::ostream& fout, std::string const& name, - std::string const& path, - cmGeneratorTarget const* t) const = 0; - virtual void WriteProjectDepends(std::ostream& fout, std::string const& name, - std::string const& path, - cmGeneratorTarget const* t) const = 0; - virtual void WriteProjectConfigurations( - std::ostream& fout, std::string const& name, - cmGeneratorTarget const& target, std::vector const& configs, - std::set const& configsPartOfDefaultBuild, - std::string const& platformMapping = "") const = 0; - virtual void WriteSLNGlobalSections(std::ostream& fout, - cmLocalGenerator* root) const; - virtual void WriteSLNFooter(std::ostream& fout) const; - - VSFolders CreateSolutionFolders( - OrderedTargetDependSet const& orderedProjectTargets); - - virtual void WriteTargetsToSolution( - std::ostream& fout, cmLocalGenerator* root, - OrderedTargetDependSet const& projectTargets) const; - virtual void WriteTargetConfigurations( - std::ostream& fout, std::vector const& configs, - OrderedTargetDependSet const& projectTargets) const; - - virtual void WriteExternalProject( - std::ostream& fout, std::string const& name, std::string const& path, - cmValue typeGuid, - std::set>> const& dependencies) const = 0; - - std::string ConvertToSolutionPath(std::string const& path) const; - - std::set IsPartOfDefaultBuild( - std::vector const& configs, - OrderedTargetDependSet const& projectTargets, - cmGeneratorTarget const* target) const; - bool IsDependedOn(OrderedTargetDependSet const& projectTargets, - cmGeneratorTarget const* target) const; - std::map GUIDMap; - - virtual void WriteFolders(std::ostream& fout, - VSFolders const& vsFolders) const; - virtual void WriteFoldersContent(std::ostream& fout, - VSFolders const& vsFolders) const; - - virtual void AddSolutionItems(cmLocalGenerator* root, - VSFolders& vsFolders) = 0; - virtual void WriteFolderSolutionItems( - std::ostream& fout, cmVisualStudioFolder const& folder) const = 0; - bool MarmasmEnabled; bool MasmEnabled; bool NasmEnabled; diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx index 5642a03d56..2c5f1f69b3 100644 --- a/Source/cmGlobalVisualStudio8Generator.cxx +++ b/Source/cmGlobalVisualStudio8Generator.cxx @@ -40,9 +40,8 @@ struct cmIDEFlagTable; cmGlobalVisualStudio8Generator::cmGlobalVisualStudio8Generator( cmake* cm, std::string const& name) - : cmGlobalVisualStudio71Generator(cm) + : cmGlobalVisualStudio7Generator(cm) { - this->ProjectConfigurationSectionName = "ProjectConfigurationPlatforms"; this->Name = name; this->ExtraFlagTable = cmGlobalVisualStudio8Generator::GetExtraFlagTableVS8(); @@ -62,7 +61,7 @@ std::string cmGlobalVisualStudio8Generator::FindDevEnvCommand() return vsxcmd; } // Now look for devenv. - return this->cmGlobalVisualStudio71Generator::FindDevEnvCommand(); + return this->cmGlobalVisualStudio7Generator::FindDevEnvCommand(); } void cmGlobalVisualStudio8Generator::EnableLanguage( @@ -218,12 +217,6 @@ std::string cmGlobalVisualStudio8Generator::GetGenerateStampList() return "generate.stamp.list"; } -bool cmGlobalVisualStudio8Generator::UseFolderProperty() const -{ - // NOLINTNEXTLINE(bugprone-parent-virtual-call) - return IsExpressEdition() ? false : cmGlobalGenerator::UseFolderProperty(); -} - bool cmGlobalVisualStudio8Generator::AddCheckTarget() { // Add a special target on which all other targets depend that @@ -362,59 +355,6 @@ void cmGlobalVisualStudio8Generator::AddExtraIDETargets() } } -void cmGlobalVisualStudio8Generator::WriteSolutionConfigurations( - std::ostream& fout, std::vector const& configs) const -{ - fout << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n"; - for (std::string const& i : configs) { - fout << "\t\t" << i << '|' << this->GetPlatformName() << " = " << i << '|' - << this->GetPlatformName() << '\n'; - } - fout << "\tEndGlobalSection\n"; -} - -void cmGlobalVisualStudio8Generator::WriteProjectConfigurations( - std::ostream& fout, std::string const& name, cmGeneratorTarget const& target, - std::vector const& configs, - std::set const& configsPartOfDefaultBuild, - std::string const& platformMapping) const -{ - std::string guid = this->GetGUID(name); - for (std::string const& i : configs) { - cmList mapConfig; - char const* dstConfig = i.c_str(); - if (target.GetProperty("EXTERNAL_MSPROJECT")) { - if (cmValue m = target.GetProperty( - cmStrCat("MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(i)))) { - mapConfig.assign(*m); - if (!mapConfig.empty()) { - dstConfig = mapConfig[0].c_str(); - } - } - } - fout << "\t\t{" << guid << "}." << i << '|' << this->GetPlatformName() - << ".ActiveCfg = " << dstConfig << '|' - << (!platformMapping.empty() ? platformMapping - : this->GetPlatformName()) - << '\n'; - auto ci = configsPartOfDefaultBuild.find(i); - if (!(ci == configsPartOfDefaultBuild.end())) { - fout << "\t\t{" << guid << "}." << i << '|' << this->GetPlatformName() - << ".Build.0 = " << dstConfig << '|' - << (!platformMapping.empty() ? platformMapping - : this->GetPlatformName()) - << '\n'; - } - if (this->NeedsDeploy(target, dstConfig)) { - fout << "\t\t{" << guid << "}." << i << '|' << this->GetPlatformName() - << ".Deploy.0 = " << dstConfig << '|' - << (!platformMapping.empty() ? platformMapping - : this->GetPlatformName()) - << '\n'; - } - } -} - bool cmGlobalVisualStudio8Generator::NeedsDeploy( cmGeneratorTarget const& target, char const* config) const { @@ -450,21 +390,6 @@ bool cmGlobalVisualStudio8Generator::TargetSystemSupportsDeployment() const return this->TargetsWindowsCE(); } -void cmGlobalVisualStudio8Generator::WriteProjectDepends( - std::ostream& fout, std::string const&, std::string const&, - cmGeneratorTarget const* gt) const -{ - TargetDependSet const& unordered = this->GetTargetDirectDepends(gt); - OrderedTargetDependSet depends(unordered, std::string()); - for (cmTargetDepend const& i : depends) { - if (!this->IsInSolution(i)) { - continue; - } - std::string guid = this->GetGUID(i->GetName()); - fout << "\t\t{" << guid << "} = {" << guid << "}\n"; - } -} - bool cmGlobalVisualStudio8Generator::NeedLinkLibraryDependencies( cmGeneratorTarget* target) { diff --git a/Source/cmGlobalVisualStudio8Generator.h b/Source/cmGlobalVisualStudio8Generator.h index 85e6ceb679..dccbc36280 100644 --- a/Source/cmGlobalVisualStudio8Generator.h +++ b/Source/cmGlobalVisualStudio8Generator.h @@ -9,7 +9,7 @@ #include -#include "cmGlobalVisualStudio71Generator.h" +#include "cmGlobalVisualStudio7Generator.h" class cmGeneratorTarget; class cmMakefile; @@ -21,7 +21,7 @@ struct cmIDEFlagTable; * * cmGlobalVisualStudio8Generator manages UNIX build process for a tree */ -class cmGlobalVisualStudio8Generator : public cmGlobalVisualStudio71Generator +class cmGlobalVisualStudio8Generator : public cmGlobalVisualStudio7Generator { public: //! Get the name for the generator. @@ -64,27 +64,12 @@ protected: bool AddCheckTarget(); - /** Return true if the configuration needs to be deployed */ - virtual bool NeedsDeploy(cmGeneratorTarget const& target, - char const* config) const; + bool NeedsDeploy(cmGeneratorTarget const& target, + char const* config) const override; - /** Returns true if the target system support debugging deployment. */ - virtual bool TargetSystemSupportsDeployment() const; + bool TargetSystemSupportsDeployment() const override; static cmIDEFlagTable const* GetExtraFlagTableVS8(); - void WriteSolutionConfigurations( - std::ostream& fout, - std::vector const& configs) const override; - void WriteProjectConfigurations( - std::ostream& fout, std::string const& name, - cmGeneratorTarget const& target, std::vector const& configs, - std::set const& configsPartOfDefaultBuild, - std::string const& platformMapping = "") const override; - void WriteProjectDepends(std::ostream& fout, std::string const& name, - std::string const& path, - cmGeneratorTarget const* t) const override; - - bool UseFolderProperty() const override; std::string Name; std::string WindowsCEVersion; diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index 8c7612ea85..370a706bc1 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -3,6 +3,7 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmGlobalVisualStudioGenerator.h" +#include #include #include #include @@ -34,8 +35,17 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cmUuid.h" #include "cmake.h" +namespace { +std::string GetSLNFile(cmLocalGenerator const* root) +{ + return cmStrCat(root->GetCurrentBinaryDirectory(), '/', + root->GetProjectName(), ".sln"); +} +} + cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator(cmake* cm) : cmGlobalGenerator(cm) { @@ -121,45 +131,6 @@ char const* cmGlobalVisualStudioGenerator::GetIDEVersion() const return ""; } -void cmGlobalVisualStudioGenerator::WriteSLNHeader(std::ostream& fout) const -{ - char utf8bom[] = { char(0xEF), char(0xBB), char(0xBF) }; - fout.write(utf8bom, 3); - fout << '\n'; - - switch (this->Version) { - case cmGlobalVisualStudioGenerator::VSVersion::VS14: - // Visual Studio 14 writes .sln format 12.00 - fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; - if (this->ExpressEdition) { - fout << "# Visual Studio Express 14 for Windows Desktop\n"; - } else { - fout << "# Visual Studio 14\n"; - } - break; - case cmGlobalVisualStudioGenerator::VSVersion::VS15: - // Visual Studio 15 writes .sln format 12.00 - fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; - fout << "# Visual Studio 15\n"; - break; - case cmGlobalVisualStudioGenerator::VSVersion::VS16: - // Visual Studio 16 writes .sln format 12.00 - fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; - fout << "# Visual Studio Version 16\n"; - break; - case cmGlobalVisualStudioGenerator::VSVersion::VS17: - // Visual Studio 17 writes .sln format 12.00 - fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; - fout << "# Visual Studio Version 17\n"; - break; - case cmGlobalVisualStudioGenerator::VSVersion::VS18: - // Visual Studio 18 writes .sln format 12.00 - fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; - fout << "# Visual Studio Version 18\n"; - break; - } -} - std::string cmGlobalVisualStudioGenerator::GetRegistryBase() { return cmGlobalVisualStudioGenerator::GetRegistryBase(this->GetIDEVersion()); @@ -810,3 +781,380 @@ bool cmGlobalVisualStudioGenerator::Open(std::string const& bindir, return std::async(std::launch::async, OpenSolution, sln).get(); } + +bool cmGlobalVisualStudioGenerator::IsDependedOn( + TargetDependSet const& projectTargets, cmGeneratorTarget const* gtIn) const +{ + return std::any_of(projectTargets.begin(), projectTargets.end(), + [this, gtIn](cmTargetDepend const& l) { + TargetDependSet const& tgtdeps = + this->GetTargetDirectDepends(l); + return tgtdeps.count(gtIn); + }); +} + +std::set cmGlobalVisualStudioGenerator::IsPartOfDefaultBuild( + std::vector const& configs, + TargetDependSet const& projectTargets, cmGeneratorTarget const* target) const +{ + std::set activeConfigs; + // if it is a utility target then only make it part of the + // default build if another target depends on it + int type = target->GetType(); + if (type == cmStateEnums::GLOBAL_TARGET) { + std::vector targetNames; + targetNames.push_back("INSTALL"); + targetNames.push_back("PACKAGE"); + for (std::string const& t : targetNames) { + // check if target is part of default build + if (target->GetName() == t) { + std::string const propertyName = + cmStrCat("CMAKE_VS_INCLUDE_", t, "_TO_DEFAULT_BUILD"); + // inspect CMAKE_VS_INCLUDE__TO_DEFAULT_BUILD properties + for (std::string const& i : configs) { + cmValue propertyValue = + target->Target->GetMakefile()->GetDefinition(propertyName); + if (propertyValue && + cmIsOn(cmGeneratorExpression::Evaluate( + *propertyValue, target->GetLocalGenerator(), i))) { + activeConfigs.insert(i); + } + } + } + } + return activeConfigs; + } + if (type == cmStateEnums::UTILITY && + !this->IsDependedOn(projectTargets, target)) { + return activeConfigs; + } + // inspect EXCLUDE_FROM_DEFAULT_BUILD[_] properties + for (std::string const& i : configs) { + if (target->GetFeature("EXCLUDE_FROM_DEFAULT_BUILD", i).IsOff()) { + activeConfigs.insert(i); + } + } + return activeConfigs; +} + +std::string cmGlobalVisualStudioGenerator::GetGUID( + std::string const& name) const +{ + std::string const& guidStoreName = cmStrCat(name, "_GUID_CMAKE"); + if (cmValue storedGUID = + this->CMakeInstance->GetCacheDefinition(guidStoreName)) { + return *storedGUID; + } + // Compute a GUID that is deterministic but unique to the build tree. + std::string input = + cmStrCat(this->CMakeInstance->GetState()->GetBinaryDirectory(), '|', name); + + cmUuid uuidGenerator; + + std::vector uuidNamespace; + uuidGenerator.StringToBinary("ee30c4be-5192-4fb0-b335-722a2dffe760", + uuidNamespace); + + std::string guid = uuidGenerator.FromMd5(uuidNamespace, input); + + return cmSystemTools::UpperCase(guid); +} + +cm::VS::Solution::Folder* cmGlobalVisualStudioGenerator::CreateSolutionFolder( + cm::VS::Solution& solution, cm::string_view rawName) const +{ + cm::VS::Solution::Folder* folder = nullptr; + std::string canonicalName; + for (std::string::size_type cur = 0;;) { + static std::string delims = "/\\"; + cur = rawName.find_first_not_of(delims, cur); + if (cur == std::string::npos) { + break; + } + std::string::size_type end = rawName.find_first_of(delims, cur); + cm::string_view f = end == std::string::npos + ? rawName.substr(cur) + : rawName.substr(cur, end - cur); + canonicalName = + canonicalName.empty() ? std::string(f) : cmStrCat(canonicalName, '/', f); + cm::VS::Solution::Folder* nextFolder = solution.GetFolder(canonicalName); + if (nextFolder->Id.empty()) { + nextFolder->Id = + this->GetGUID(cmStrCat("CMAKE_FOLDER_GUID_"_s, canonicalName)); + if (folder) { + folder->Folders.emplace_back(nextFolder); + } + solution.Folders.emplace_back(nextFolder); + } + folder = nextFolder; + cur = end; + } + return folder; +} + +cm::VS::Solution cmGlobalVisualStudioGenerator::CreateSolution( + cmLocalGenerator const* root, TargetDependSet const& projectTargets) const +{ + using namespace cm::VS; + Solution solution; + solution.VSVersion = this->Version; + solution.VSExpress = + this->ExpressEdition ? VersionExpress::Yes : VersionExpress::No; + solution.Platform = this->GetPlatformName(); + solution.Configs = + root->GetMakefile()->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); + solution.StartupProject = this->GetStartupProjectName(root); + + auto addProject = [this, useFolders = this->UseFolderProperty(), + &solution](cmGeneratorTarget const* gt, + Solution::Project const* p) { + if (Solution::Folder* const folder = useFolders + ? this->CreateSolutionFolder(solution, gt->GetEffectiveFolderName()) + : nullptr) { + folder->Projects.emplace_back(p); + } else { + solution.Projects.emplace_back(p); + } + }; + + for (cmTargetDepend const& projectTarget : projectTargets) { + cmGeneratorTarget const* gt = projectTarget; + if (!this->IsInSolution(gt)) { + continue; + } + + Solution::Project* project = solution.GetProject(gt->GetName()); + project->Id = this->GetGUID(gt->GetName()); + + std::set const& includeConfigs = + this->IsPartOfDefaultBuild(solution.Configs, projectTargets, gt); + auto addProjectConfig = + [this, project, gt, &includeConfigs](std::string const& solutionConfig, + std::string const& projectConfig) { + bool const build = + includeConfigs.find(solutionConfig) != includeConfigs.end(); + bool const deploy = this->NeedsDeploy(*gt, solutionConfig.c_str()); + project->Configs.emplace_back( + Solution::ProjectConfig{ projectConfig, build, deploy }); + }; + + if (cmValue expath = gt->GetProperty("EXTERNAL_MSPROJECT")) { + project->Path = *expath; + cmValue const projectType = gt->GetProperty("VS_PROJECT_TYPE"); + if (!projectType.IsEmpty()) { + project->TypeId = *projectType; + } else { + project->TypeId = Solution::Project::TypeIdDefault; + } + for (std::string const& config : solution.Configs) { + cmList mapConfig{ gt->GetProperty(cmStrCat( + "MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(config))) }; + addProjectConfig(config, !mapConfig.empty() ? mapConfig[0] : config); + } + cmValue platformMapping = gt->GetProperty("VS_PLATFORM_MAPPING"); + project->Platform = + !platformMapping.IsEmpty() ? *platformMapping : solution.Platform; + for (BT> const& i : gt->GetUtilities()) { + std::string const& dep = i.Value.first; + if (this->IsDepInSolution(dep)) { + project->BuildDependencies.emplace_back(solution.GetProject(dep)); + } + } + addProject(gt, project); + continue; + } + + cmValue vcprojName = gt->GetProperty("GENERATOR_FILE_NAME"); + cmValue vcprojType = gt->GetProperty("GENERATOR_FILE_NAME_EXT"); + if (vcprojName && vcprojType) { + cmLocalGenerator* lg = gt->GetLocalGenerator(); + std::string dir = + root->MaybeRelativeToCurBinDir(lg->GetCurrentBinaryDirectory()); + if (dir == "."_s) { + dir.clear(); + } else if (!cmHasLiteralSuffix(dir, "/")) { + dir += "/"; + } + + project->Path = cmStrCat(dir, *vcprojName, *vcprojType); + if (this->TargetIsFortranOnly(gt)) { + project->TypeId = Solution::Project::TypeIdFortran; + } else if (gt->IsCSharpOnly()) { + project->TypeId = Solution::Project::TypeIdCSharp; + } else { + project->TypeId = Solution::Project::TypeIdDefault; + } + + project->Platform = + // On VS 19 and above, always map .NET SDK projects to "Any CPU". + (gt->IsDotNetSdkTarget() && this->Version >= VSVersion::VS16 && + !cmGlobalVisualStudioGenerator::IsReservedTarget(gt->GetName())) + ? "Any CPU" + : solution.Platform; + + // Add solution-level dependencies. + TargetDependSet const& depends = this->GetTargetDirectDepends(gt); + for (cmTargetDepend const& dep : depends) { + if (this->IsInSolution(dep)) { + project->BuildDependencies.emplace_back( + solution.GetProject(dep->GetName())); + } + } + + for (std::string const& config : solution.Configs) { + addProjectConfig(config, config); + } + + addProject(gt, project); + continue; + } + } + + cmMakefile* mf = root->GetMakefile(); + // Unfortunately we have to copy the source groups because + // FindSourceGroup uses a regex which is modifying the group. + std::vector sourceGroups = mf->GetSourceGroups(); + std::vector items = + cmList{ root->GetMakefile()->GetProperty("VS_SOLUTION_ITEMS") }; + for (std::string item : items) { + if (!cmSystemTools::FileIsFullPath(item)) { + item = + cmSystemTools::CollapseFullPath(item, mf->GetCurrentSourceDirectory()); + } + cmSourceGroup* sg = mf->FindSourceGroup(item, sourceGroups); + std::string folderName = sg->GetFullName(); + if (folderName.empty()) { + folderName = "Solution Items"_s; + } + Solution::Folder* folder = + this->CreateSolutionFolder(solution, folderName); + folder->Files.emplace(std::move(item)); + } + + Solution::PropertyGroup* pgExtensibilityGlobals = nullptr; + Solution::PropertyGroup* pgExtensibilityAddIns = nullptr; + std::vector const propKeys = + root->GetMakefile()->GetPropertyKeys(); + for (std::string const& it : propKeys) { + if (!cmHasLiteralPrefix(it, "VS_GLOBAL_SECTION_")) { + continue; + } + std::string name = it.substr(18); + Solution::PropertyGroup::Load scope; + if (cmHasLiteralPrefix(name, "PRE_")) { + name = name.substr(4); + scope = Solution::PropertyGroup::Load::Pre; + } else if (cmHasLiteralPrefix(name, "POST_")) { + name = name.substr(5); + scope = Solution::PropertyGroup::Load::Post; + } else { + continue; + } + if (name.empty()) { + continue; + } + Solution::PropertyGroup* pg = solution.GetPropertyGroup(name); + solution.PropertyGroups.emplace_back(pg); + pg->Scope = scope; + cmList keyValuePairs{ root->GetMakefile()->GetProperty(it) }; + for (std::string const& itPair : keyValuePairs) { + std::string::size_type const posEqual = itPair.find('='); + if (posEqual != std::string::npos) { + std::string key = cmTrimWhitespace(itPair.substr(0, posEqual)); + std::string value = cmTrimWhitespace(itPair.substr(posEqual + 1)); + pg->Map.emplace(std::move(key), std::move(value)); + } + } + if (name == "ExtensibilityGlobals"_s) { + pgExtensibilityGlobals = pg; + } else if (name == "ExtensibilityAddIns"_s) { + pgExtensibilityAddIns = pg; + } + } + + if (!pgExtensibilityGlobals) { + pgExtensibilityGlobals = + solution.GetPropertyGroup("ExtensibilityGlobals"_s); + solution.PropertyGroups.emplace_back(pgExtensibilityGlobals); + } + std::string const solutionGuid = + this->GetGUID(cmStrCat(root->GetProjectName(), ".sln")); + pgExtensibilityGlobals->Map.emplace("SolutionGuid", + cmStrCat('{', solutionGuid, '}')); + + if (!pgExtensibilityAddIns) { + pgExtensibilityAddIns = solution.GetPropertyGroup("ExtensibilityAddIns"_s); + solution.PropertyGroups.emplace_back(pgExtensibilityAddIns); + } + + solution.CanonicalizeOrder(); + + return solution; +} + +void cmGlobalVisualStudioGenerator::Generate() +{ + // first do the superclass method + this->cmGlobalGenerator::Generate(); + + // Now write out the VS Solution files. + for (auto& it : this->ProjectMap) { + this->GenerateSolution(it.second[0], it.second); + } + + // If any solution or project files changed during the generation, + // tell Visual Studio to reload them... + if (!cmSystemTools::GetErrorOccurredFlag() && + !this->LocalGenerators.empty()) { + this->CallVisualStudioMacro(MacroReload, + GetSLNFile(this->LocalGenerators[0].get())); + } + + if (this->Version == VSVersion::VS14 && + !this->CMakeInstance->GetIsInTryCompile()) { + std::string cmakeWarnVS14; + if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue( + "CMAKE_WARN_VS14")) { + this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS14"); + cmakeWarnVS14 = *cached; + } else { + cmSystemTools::GetEnv("CMAKE_WARN_VS14", cmakeWarnVS14); + } + if (cmakeWarnVS14.empty() || !cmIsOff(cmakeWarnVS14)) { + this->CMakeInstance->IssueMessage( + MessageType::WARNING, + "The \"Visual Studio 14 2015\" generator is deprecated " + "and will be removed in a future version of CMake." + "\n" + "Add CMAKE_WARN_VS14=OFF to the cache to disable this warning."); + } + } +} + +void cmGlobalVisualStudioGenerator::GenerateSolution( + cmLocalGenerator const* root, + std::vector const& generators) +{ + if (generators.empty()) { + return; + } + + // Collect all targets under this root generator and the transitive + // closure of their dependencies. + TargetDependSet const projectTargets = + this->GetTargetsForProject(root, generators); + + std::string fname = GetSLNFile(root); + cmGeneratedFileStream fout(fname); + fout.SetCopyIfDifferent(true); + if (!fout) { + return; + } + + cm::VS::Solution const solution = this->CreateSolution(root, projectTargets); + WriteSln(fout, solution); + + if (fout.Close()) { + this->FileReplacedDuringGenerate(fname); + } +} diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index 344295c29c..b9b25b4e25 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -14,6 +14,8 @@ #include "cmGlobalGenerator.h" #include "cmTargetDepend.h" +#include "cmVSSolution.h" +#include "cmVSVersion.h" #include "cmValue.h" class cmCustomCommand; @@ -31,15 +33,7 @@ class cmake; class cmGlobalVisualStudioGenerator : public cmGlobalGenerator { public: - /** Known versions of Visual Studio. */ - enum class VSVersion : uint16_t - { - VS14 = 140, - VS15 = 150, - VS16 = 160, - VS17 = 170, - VS18 = 180, - }; + using VSVersion = cm::VS::Version; ~cmGlobalVisualStudioGenerator() override; @@ -159,6 +153,9 @@ public: bool IsVisualStudio() const override { return true; } + //! Lookup a stored GUID or compute one deterministically. + std::string GetGUID(std::string const& name) const; + protected: cmGlobalVisualStudioGenerator(cmake* cm); @@ -173,14 +170,37 @@ protected: char const* GetIDEVersion() const; - void WriteSLNHeader(std::ostream& fout) const; - VSVersion Version; bool ExpressEdition; std::string GeneratorPlatform; std::string DefaultPlatformName; + /** Return true if the configuration needs to be deployed */ + virtual bool NeedsDeploy(cmGeneratorTarget const& target, + char const* config) const = 0; + + /** Returns true if the target system support debugging deployment. */ + virtual bool TargetSystemSupportsDeployment() const = 0; + + std::set IsPartOfDefaultBuild( + std::vector const& configs, + TargetDependSet const& projectTargets, + cmGeneratorTarget const* target) const; + bool IsDependedOn(TargetDependSet const& projectTargets, + cmGeneratorTarget const* target) const; + std::map GUIDMap; + + cm::VS::Solution CreateSolution(cmLocalGenerator const* root, + TargetDependSet const& projectTargets) const; + cm::VS::Solution::Folder* CreateSolutionFolder( + cm::VS::Solution& solution, cm::string_view rawName) const; + + void Generate() override; + + void GenerateSolution(cmLocalGenerator const* root, + std::vector const& generators); + private: virtual std::string GetVSMakeProgram() = 0; void PrintCompilerAdvice(std::ostream&, std::string const&, diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx index bc13358602..ab76a429cb 100644 --- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx +++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx @@ -8,6 +8,7 @@ #include #include +#include #include #include "cmsys/FStream.hxx" diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.h b/Source/cmGlobalVisualStudioVersionedGenerator.h index bb1def3096..70102cfe06 100644 --- a/Source/cmGlobalVisualStudioVersionedGenerator.h +++ b/Source/cmGlobalVisualStudioVersionedGenerator.h @@ -8,6 +8,7 @@ #include #include +#include #include "cmGlobalVisualStudio10Generator.h" #include "cmGlobalVisualStudio14Generator.h" diff --git a/Source/cmVSSolution.cxx b/Source/cmVSSolution.cxx new file mode 100644 index 0000000000..99323746bf --- /dev/null +++ b/Source/cmVSSolution.cxx @@ -0,0 +1,283 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmVSSolution.h" + +#include +#include +#include +#include + +#include +#include + +#include "cmSystemTools.h" + +namespace cm { +namespace VS { + +cm::string_view const Solution::Project::TypeIdDefault = + "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942"_s; +cm::string_view const Solution::Project::TypeIdCSharp = + "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC"_s; +cm::string_view const Solution::Project::TypeIdFortran = + "6989167D-11E4-40FE-8C1A-2192A86A7E90"_s; +cm::string_view const Solution::Folder::TypeId = + "2150E333-8FDC-42A3-9474-1A3956D46DE8"_s; + +std::vector Solution::GetAllProjects() const +{ + std::vector projects; + projects.reserve(this->ProjectMap.size()); + for (Project const* project : this->Projects) { + projects.emplace_back(project); + } + for (Folder const* folder : this->Folders) { + for (Project const* project : folder->Projects) { + projects.emplace_back(project); + } + } + return projects; +} + +namespace { +template +T* GetEntry(std::map>& entryMap, + cm::string_view name) +{ + auto i = entryMap.find(name); + if (i == entryMap.end()) { + auto p = cm::make_unique(); + p->Name = name; + i = entryMap.emplace(p->Name, std::move(p)).first; + } + return i->second.get(); +} +} + +Solution::Folder* Solution::GetFolder(cm::string_view name) +{ + return GetEntry(this->FolderMap, name); +} + +Solution::Project* Solution::GetProject(cm::string_view name) +{ + return GetEntry(this->ProjectMap, name); +} + +Solution::PropertyGroup* Solution::GetPropertyGroup(cm::string_view name) +{ + return GetEntry(this->PropertyGroupMap, name); +} + +namespace { +struct OrderByName +{ + template + bool operator()(T const* l, T const* r) const + { + return l->Name < r->Name; + } +}; +} + +void Solution::CanonicalizeOrder() +{ + std::sort(this->Folders.begin(), this->Folders.end(), OrderByName()); + for (auto& fi : this->FolderMap) { + Folder* folder = fi.second.get(); + std::sort(folder->Folders.begin(), folder->Folders.end(), OrderByName()); + std::sort(folder->Projects.begin(), folder->Projects.end(), OrderByName()); + } + std::sort(this->Projects.begin(), this->Projects.end(), OrderByName()); + for (auto& pi : this->ProjectMap) { + Project* project = pi.second.get(); + std::sort(project->BuildDependencies.begin(), + project->BuildDependencies.end(), OrderByName()); + } +} + +namespace { + +void WriteSlnHeader(std::ostream& sln, Version version, VersionExpress express) +{ + char utf8bom[] = { char(0xEF), char(0xBB), char(0xBF) }; + sln.write(utf8bom, 3); + sln << '\n'; + + switch (version) { + case Version::VS14: + // Visual Studio 14 writes .sln format 12.00 + sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; + if (express == VersionExpress::Yes) { + sln << "# Visual Studio Express 14 for Windows Desktop\n"; + } else { + sln << "# Visual Studio 14\n"; + } + break; + case Version::VS15: + // Visual Studio 15 writes .sln format 12.00 + sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; + sln << "# Visual Studio 15\n"; + break; + case Version::VS16: + // Visual Studio 16 writes .sln format 12.00 + sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; + sln << "# Visual Studio Version 16\n"; + break; + case Version::VS17: + // Visual Studio 17 writes .sln format 12.00 + sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; + sln << "# Visual Studio Version 17\n"; + break; + case Version::VS18: + // Visual Studio 18 writes .sln format 12.00 + sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; + sln << "# Visual Studio Version 18\n"; + break; + } +} + +void WriteSlnProject(std::ostream& sln, Solution::Project const& project) +{ + std::string projectPath = project.Path; + std::replace(projectPath.begin(), projectPath.end(), '/', '\\'); + sln << "Project(\"{" << project.TypeId << "}\") = \"" << project.Name + << "\", \"" << projectPath << "\", \"{" << project.Id << "}\"\n"; + sln << "\tProjectSection(ProjectDependencies) = postProject\n"; + for (Solution::Project const* d : project.BuildDependencies) { + sln << "\t\t{" << d->Id << "} = {" << d->Id << "}\n"; + } + sln << "\tEndProjectSection\n"; + sln << "EndProject\n"; +} + +void WriteSlnFolder(std::ostream& sln, Solution::Folder const& folder) +{ + std::string folderName = folder.Name; + std::replace(folderName.begin(), folderName.end(), '/', '\\'); + std::string const fileName = cmSystemTools::GetFilenameName(folder.Name); + sln << "Project(\"{" << Solution::Folder::TypeId << "}\") = \"" << fileName + << "\", \"" << folderName << "\", \"{" << folder.Id << "}\"\n"; + if (!folder.Files.empty()) { + sln << "\tProjectSection(SolutionItems) = preProject\n"; + for (std::string const& item : folder.Files) { + sln << "\t\t" << item << " = " << item << "\n"; + } + sln << "\tEndProjectSection\n"; + } + sln << "EndProject\n"; +} + +void WriteSlnSolutionConfigurationPlatforms(std::ostream& sln, + Solution const& solution) +{ + sln << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n"; + for (std::string const& config : solution.Configs) { + sln << "\t\t" << config << '|' << solution.Platform << " = " << config + << '|' << solution.Platform << '\n'; + } + sln << "\tEndGlobalSection\n"; +} + +void WriteSlnProjectConfigurationPlatforms(std::ostream& sln, + Solution const& solution, + Solution::Project const& project) +{ + auto const writeStep = [&sln, &solution, &project](std::size_t i, + cm::string_view step) { + sln << "\t\t{" << project.Id << "}." << solution.Configs[i] << '|' + << solution.Platform << "." << step << " = " + << project.Configs[i].Config << '|' << project.Platform << '\n'; + }; + assert(project.Configs.size() == solution.Configs.size()); + for (std::size_t i = 0; i < solution.Configs.size(); ++i) { + writeStep(i, "ActiveCfg"_s); + if (project.Configs[i].Build) { + writeStep(i, "Build.0"_s); + } + if (project.Configs[i].Deploy) { + writeStep(i, "Deploy.0"_s); + } + } +} + +void WriteSlnProjectConfigurationPlatforms( + std::ostream& sln, Solution const& solution, + std::vector const& projects) +{ + sln << "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n"; + for (Solution::Project const* project : projects) { + WriteSlnProjectConfigurationPlatforms(sln, solution, *project); + } + sln << "\tEndGlobalSection\n"; +} + +void WriteSlnNestedProjects( + std::ostream& sln, std::vector const& folders) +{ + sln << "\tGlobalSection(NestedProjects) = preSolution\n"; + for (Solution::Folder const* folder : folders) { + for (Solution::Folder const* nestedFolder : folder->Folders) { + sln << "\t\t{" << nestedFolder->Id << "} = {" << folder->Id << "}\n"; + } + for (Solution::Project const* project : folder->Projects) { + sln << "\t\t{" << project->Id << "} = {" << folder->Id << "}\n"; + } + } + sln << "\tEndGlobalSection\n"; +} + +void WriteSlnPropertyGroup(std::ostream& sln, + Solution::PropertyGroup const& pg) +{ + cm::string_view const order = pg.Scope == Solution::PropertyGroup::Load::Pre + ? "preSolution"_s + : "postSolution"_s; + sln << "\tGlobalSection(" << pg.Name << ") = " << order << '\n'; + for (auto const& i : pg.Map) { + sln << "\t\t" << i.first << " = " << i.second << '\n'; + } + sln << "\tEndGlobalSection\n"; +} + +} + +void WriteSln(std::ostream& sln, Solution const& solution) +{ + assert(solution.VSVersion); + assert(solution.VSExpress); + + std::vector projects = solution.GetAllProjects(); + std::sort(projects.begin(), projects.end(), + [&solution](Solution::Project const* l, + Solution::Project const* r) -> bool { + if (r->Name == solution.StartupProject) { + return false; + } + if (l->Name == solution.StartupProject) { + return true; + } + return l->Name < r->Name; + }); + + WriteSlnHeader(sln, *solution.VSVersion, *solution.VSExpress); + for (Solution::Folder const* folder : solution.Folders) { + WriteSlnFolder(sln, *folder); + } + for (Solution::Project const* project : projects) { + WriteSlnProject(sln, *project); + } + sln << "Global\n"; + WriteSlnSolutionConfigurationPlatforms(sln, solution); + WriteSlnProjectConfigurationPlatforms(sln, solution, projects); + if (!solution.Folders.empty()) { + WriteSlnNestedProjects(sln, solution.Folders); + } + for (Solution::PropertyGroup const* pg : solution.PropertyGroups) { + WriteSlnPropertyGroup(sln, *pg); + } + sln << "EndGlobal\n"; +} + +} +} diff --git a/Source/cmVSSolution.h b/Source/cmVSSolution.h new file mode 100644 index 0000000000..adc5127200 --- /dev/null +++ b/Source/cmVSSolution.h @@ -0,0 +1,169 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include +#include +#include +#include + +#include +#include +#include + +#include "cmVSVersion.h" + +namespace cm { +namespace VS { + +/** Represent a Visual Studio Solution. + In VS terminology, a "project" corresponds to a CMake "target". */ +struct Solution final +{ + Solution() = default; + Solution(Solution&&) = default; + Solution& operator=(Solution&&) = default; + + /** Represent how a project behaves under one solution config. */ + struct ProjectConfig final + { + /** Project-specific config corresponding to this solution config. + This is usually the same as the solution config, but it can map + to another config in some cases. */ + std::string Config; + + /** Does the project build under this solution config? */ + bool Build = false; + + /** Does the project deploy under this solution config? */ + bool Deploy = false; + }; + + /** Represent one project in a Solution. + This corresponds to one CMake "target". */ + struct Project final + { + /** Project name. This corresponds to the CMake "target" name. */ + std::string Name; + + /** Project GUID. */ + std::string Id; + + /** Project type GUID. */ + std::string TypeId; + + /** Path to the project file on disk. + This is either absolute or relative to the Solution file. */ + std::string Path; + + /** Project-specific platform. This is usually the same as the + Solution::Platform, but it can be different in some cases. */ + std::string Platform; + + /** Project-specific configuration corresponding to each solution config. + This vector has the same length as the Solution::Configs vector. */ + std::vector Configs; + + /** Solution-level dependencies of the project on other projects. */ + std::vector BuildDependencies; + + // Project type GUIDs used during creation. + static cm::string_view const TypeIdDefault; + static cm::string_view const TypeIdCSharp; + static cm::string_view const TypeIdFortran; + }; + + /** Represent one folder in a Solution. */ + struct Folder final + { + /** Canonical folder name. This includes parent folders separated by + forward slashes. */ + std::string Name; + + /** Folder GUID. */ + std::string Id; + + /** List of folders contained inside this folder. */ + std::vector Folders; + + /** List of projects contained inside this folder. */ + std::vector Projects; + + /** Solution-level files contained inside this folder. */ + std::set Files; + + // Folder type GUID. + static cm::string_view const TypeId; + }; + + /** Represent a group of solution-level Properties. */ + struct PropertyGroup final + { + enum class Load + { + Pre, + Post, + }; + + /** Properties group name. */ + std::string Name; + + /** Properties group load behavior. */ + Load Scope = Load::Post; + + /** Property key-value pairs in the group. */ + std::map Map; + }; + + /** Visual Studio major version number, if known. */ + cm::optional VSVersion; + + /** Whether this is a VS Express edition, if known. */ + cm::optional VSExpress; + + /** Solution-wide target platform. This is a Windows architecture. */ + std::string Platform; + + /** Solution-wide build configurations. + This corresponds to CMAKE_CONFIGURATION_TYPES. */ + std::vector Configs; + + /** List of all folders in the solution. */ + std::vector Folders; + + /** List of projects in the solution that are not in folders. */ + std::vector Projects; + + /** List of solution-level property groups. */ + std::vector PropertyGroups; + + /** Name of the default startup project. */ + std::string StartupProject; + + /** Get all projects in the solution, including all folders. */ + std::vector GetAllProjects() const; + + // Non-const methods used during creation. + Folder* GetFolder(cm::string_view name); + Project* GetProject(cm::string_view name); + PropertyGroup* GetPropertyGroup(cm::string_view name); + void CanonicalizeOrder(); + +private: + Solution(Solution const&) = delete; + Solution& operator=(Solution const&) = delete; + + // Own and index named entities. + // The string_view keys point at the Name members. + std::map> FolderMap; + std::map> ProjectMap; + std::map> PropertyGroupMap; +}; + +/** Write the .sln-format representation. */ +void WriteSln(std::ostream& sln, Solution const& solution); + +} +} diff --git a/Source/cmVSVersion.h b/Source/cmVSVersion.h new file mode 100644 index 0000000000..756a06431b --- /dev/null +++ b/Source/cmVSVersion.h @@ -0,0 +1,25 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include + +namespace cm { +namespace VS { +/** Known versions of Visual Studio. */ +enum class Version : std::uint16_t +{ + VS14 = 140, + VS15 = 150, + VS16 = 160, + VS17 = 170, + VS18 = 180, +}; + +enum class VersionExpress +{ + No, + Yes, +}; +} +} diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake index 22db7a586f..2d0c96e1a0 100644 --- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake +++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake @@ -18,7 +18,6 @@ run_cmake(ExplicitCMakeLists) run_cmake(InterfaceLibSources) run_cmake(NoImpLib) run_cmake(RuntimeLibrary) -run_cmake(SolutionItems) run_cmake(SourceGroupCMakeLists) run_cmake(SourceGroupTreeCMakeLists) run_cmake(SourceGroupFileSet) @@ -45,7 +44,6 @@ run_cmake(VsDpiAwareBadParam) run_cmake(VsForceInclude) run_cmake(VsPrecompileHeaders) run_cmake(VsPrecompileHeadersShort) -run_cmake(VsDeployEnabled) run_cmake(VsSettings) run_cmake(VsSourceSettingsTool) run_cmake(VsPlatformToolset) diff --git a/Tests/RunCMake/VS10Project/VsDeployEnabled-check.cmake b/Tests/RunCMake/VSSolution/DeployEnabled-check.cmake similarity index 96% rename from Tests/RunCMake/VS10Project/VsDeployEnabled-check.cmake rename to Tests/RunCMake/VSSolution/DeployEnabled-check.cmake index 0ff8678e24..22b824c365 100644 --- a/Tests/RunCMake/VS10Project/VsDeployEnabled-check.cmake +++ b/Tests/RunCMake/VSSolution/DeployEnabled-check.cmake @@ -7,7 +7,7 @@ endif() # Test solution file for deployment. # -set(vcSlnFile "${RunCMake_TEST_BINARY_DIR}/VsDeployEnabled.sln") +set(vcSlnFile "${RunCMake_TEST_BINARY_DIR}/DeployEnabled.sln") if(NOT EXISTS "${vcSlnFile}") set(RunCMake_TEST_FAILED "Solution file ${vcSlnFile} does not exist.") return() diff --git a/Tests/RunCMake/VS10Project/VsDeployEnabled.cmake b/Tests/RunCMake/VSSolution/DeployEnabled.cmake similarity index 100% rename from Tests/RunCMake/VS10Project/VsDeployEnabled.cmake rename to Tests/RunCMake/VSSolution/DeployEnabled.cmake diff --git a/Tests/RunCMake/VSSolution/RunCMakeTest.cmake b/Tests/RunCMake/VSSolution/RunCMakeTest.cmake index 8737c9a2f9..328a0aaf98 100644 --- a/Tests/RunCMake/VSSolution/RunCMakeTest.cmake +++ b/Tests/RunCMake/VSSolution/RunCMakeTest.cmake @@ -1,6 +1,7 @@ include(RunCMake) include(${CMAKE_CURRENT_LIST_DIR}/solution_parsing.cmake) +run_cmake(DeployEnabled) run_cmake(OnePre) run_cmake(OnePost) run_cmake(MorePre) @@ -9,6 +10,7 @@ run_cmake(PrePost) run_cmake(Override1) run_cmake(Override2) run_cmake(Override3) +run_cmake(SolutionItems) run_cmake(StartupProject) run_cmake(StartupProjectMissing) run_cmake(AddPackageToDefault) diff --git a/Tests/RunCMake/VS10Project/SolutionItems-check.cmake b/Tests/RunCMake/VSSolution/SolutionItems-check.cmake similarity index 100% rename from Tests/RunCMake/VS10Project/SolutionItems-check.cmake rename to Tests/RunCMake/VSSolution/SolutionItems-check.cmake diff --git a/Tests/RunCMake/VS10Project/SolutionItems.cmake b/Tests/RunCMake/VSSolution/SolutionItems.cmake similarity index 100% rename from Tests/RunCMake/VS10Project/SolutionItems.cmake rename to Tests/RunCMake/VSSolution/SolutionItems.cmake diff --git a/Tests/RunCMake/VS10Project/SolutionItems/CMakeLists.txt b/Tests/RunCMake/VSSolution/SolutionItems/CMakeLists.txt similarity index 100% rename from Tests/RunCMake/VS10Project/SolutionItems/CMakeLists.txt rename to Tests/RunCMake/VSSolution/SolutionItems/CMakeLists.txt diff --git a/Tests/RunCMake/VS10Project/SolutionItems/extraneous.txt b/Tests/RunCMake/VSSolution/SolutionItems/extraneous.txt similarity index 100% rename from Tests/RunCMake/VS10Project/SolutionItems/extraneous.txt rename to Tests/RunCMake/VSSolution/SolutionItems/extraneous.txt diff --git a/Tests/RunCMake/VS10Project/solution-item-0-1.txt b/Tests/RunCMake/VSSolution/foo.cpp similarity index 100% rename from Tests/RunCMake/VS10Project/solution-item-0-1.txt rename to Tests/RunCMake/VSSolution/foo.cpp diff --git a/Tests/RunCMake/VS10Project/solution-item-1-1.txt b/Tests/RunCMake/VSSolution/solution-item-0-1.txt similarity index 100% rename from Tests/RunCMake/VS10Project/solution-item-1-1.txt rename to Tests/RunCMake/VSSolution/solution-item-0-1.txt diff --git a/Tests/RunCMake/VS10Project/solution-item-2-1.txt b/Tests/RunCMake/VSSolution/solution-item-1-1.txt similarity index 100% rename from Tests/RunCMake/VS10Project/solution-item-2-1.txt rename to Tests/RunCMake/VSSolution/solution-item-1-1.txt diff --git a/Tests/RunCMake/VS10Project/solution-item-2-2.txt b/Tests/RunCMake/VSSolution/solution-item-2-1.txt similarity index 100% rename from Tests/RunCMake/VS10Project/solution-item-2-2.txt rename to Tests/RunCMake/VSSolution/solution-item-2-1.txt diff --git a/Tests/RunCMake/VSSolution/solution-item-2-2.txt b/Tests/RunCMake/VSSolution/solution-item-2-2.txt new file mode 100644 index 0000000000..e69de29bb2