Explorar el Código

Merge topic 'vs-sln'

6a77c80834 cmGlobalVisualStudioGenerator: Consolidate solution generation methods
6b1c101410 cmGlobalVisualStudioGenerator: Adopt more solution generation methods
772cf917c1 cmGlobalVisualStudio71Generator: Remove this now-unnecessary class
3882718872 VS: Decouple solution generation from `.sln` file format
d67e7d2726 VS: Factor out Visual Studio `Version` enumeration
76266f9df6 cmGlobalVisualStudioGenerator: Adopt some solution generation methods
d52eb1d083 cmGlobalVisualStudioGenerator: Remove unused folder support rules
50f87b4bf2 cmGlobalGenerator: Simplify collection of targets under a project()
...

Acked-by: Kitware Robot <[email protected]>
Merge-request: !11196
Brad King hace 1 mes
padre
commit
503ab73ee4
Se han modificado 35 ficheros con 926 adiciones y 950 borrados
  1. 3 2
      Source/CMakeLists.txt
  2. 6 6
      Source/cmGlobalGenerator.cxx
  3. 7 6
      Source/cmGlobalGenerator.h
  4. 2 3
      Source/cmGlobalGhsMultiGenerator.cxx
  5. 1 2
      Source/cmGlobalVisualStudio10Generator.cxx
  6. 0 9
      Source/cmGlobalVisualStudio11Generator.cxx
  7. 0 1
      Source/cmGlobalVisualStudio11Generator.h
  8. 0 57
      Source/cmGlobalVisualStudio14Generator.cxx
  9. 0 5
      Source/cmGlobalVisualStudio14Generator.h
  10. 0 168
      Source/cmGlobalVisualStudio71Generator.cxx
  11. 0 53
      Source/cmGlobalVisualStudio71Generator.h
  12. 0 415
      Source/cmGlobalVisualStudio7Generator.cxx
  13. 0 73
      Source/cmGlobalVisualStudio7Generator.h
  14. 2 77
      Source/cmGlobalVisualStudio8Generator.cxx
  15. 5 20
      Source/cmGlobalVisualStudio8Generator.h
  16. 387 39
      Source/cmGlobalVisualStudioGenerator.cxx
  17. 31 11
      Source/cmGlobalVisualStudioGenerator.h
  18. 1 0
      Source/cmGlobalVisualStudioVersionedGenerator.cxx
  19. 1 0
      Source/cmGlobalVisualStudioVersionedGenerator.h
  20. 283 0
      Source/cmVSSolution.cxx
  21. 169 0
      Source/cmVSSolution.h
  22. 25 0
      Source/cmVSVersion.h
  23. 0 2
      Tests/RunCMake/VS10Project/RunCMakeTest.cmake
  24. 1 1
      Tests/RunCMake/VSSolution/DeployEnabled-check.cmake
  25. 0 0
      Tests/RunCMake/VSSolution/DeployEnabled.cmake
  26. 2 0
      Tests/RunCMake/VSSolution/RunCMakeTest.cmake
  27. 0 0
      Tests/RunCMake/VSSolution/SolutionItems-check.cmake
  28. 0 0
      Tests/RunCMake/VSSolution/SolutionItems.cmake
  29. 0 0
      Tests/RunCMake/VSSolution/SolutionItems/CMakeLists.txt
  30. 0 0
      Tests/RunCMake/VSSolution/SolutionItems/extraneous.txt
  31. 0 0
      Tests/RunCMake/VSSolution/foo.cpp
  32. 0 0
      Tests/RunCMake/VSSolution/solution-item-0-1.txt
  33. 0 0
      Tests/RunCMake/VSSolution/solution-item-1-1.txt
  34. 0 0
      Tests/RunCMake/VSSolution/solution-item-2-1.txt
  35. 0 0
      Tests/RunCMake/VSSolution/solution-item-2-2.txt

+ 3 - 2
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

+ 6 - 6
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<cmLocalGenerator*>& generators)
+cmGlobalGenerator::TargetDependSet cmGlobalGenerator::GetTargetsForProject(
+  cmLocalGenerator const* root,
+  std::vector<cmLocalGenerator*> 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) {

+ 7 - 6
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<cmLocalGenerator*>& 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<cmLocalGenerator*> 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);

+ 2 - 3
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) {

+ 1 - 2
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)

+ 0 - 9
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<std::string>
 cmGlobalVisualStudio11Generator::GetInstalledWindowsCESDKs()
 {

+ 0 - 1
Source/cmGlobalVisualStudio11Generator.h

@@ -47,7 +47,6 @@ protected:
   bool IsWindowsPhoneToolsetInstalled() const;
   bool IsWindowsStoreToolsetInstalled() const;
 
-  bool UseFolderProperty() const override;
   static std::set<std::string> GetInstalledWindowsCESDKs();
 
   /** Return true if target system supports debugging deployment. */

+ 0 - 57
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<cmSourceGroup> sourceGroups = makefile->GetSourceGroups();
-
-    cmVisualStudioFolder* defaultFolder = nullptr;
-
-    std::vector<std::string> 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";
-}

+ 0 - 5
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;

+ 0 - 168
Source/cmGlobalVisualStudio71Generator.cxx

@@ -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 <map>
-#include <sstream>
-
-#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<std::string> 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<BT<std::pair<std::string, bool>>> 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<std::pair<std::string, bool>> 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<std::string> const& configs,
-  std::set<std::string> 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;
-    }
-  }
-}

+ 0 - 53
Source/cmGlobalVisualStudio71Generator.h

@@ -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 <iosfwd>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "cmGlobalVisualStudio7Generator.h"
-#include "cmValue.h"
-
-class cmGeneratorTarget;
-class cmLocalGenerator;
-class cmake;
-template <typename T>
-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<std::string> 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<std::string> const& configs,
-    std::set<std::string> 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<BT<std::pair<std::string, bool>>> const& depends) const override;
-
-  // Folders are not supported by VS 7.1.
-  bool UseFolderProperty() const override { return false; }
-
-  std::string ProjectConfigurationSectionName;
-};

+ 0 - 415
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<cmLocalGenerator*>& 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<unsigned char> 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<std::string> cmGlobalVisualStudio7Generator::IsPartOfDefaultBuild(
-  std::vector<std::string> const& configs,
-  OrderedTargetDependSet const& projectTargets,
-  cmGeneratorTarget const* target) const
-{
-  std::set<std::string> 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<std::string> targetNames;
-    targetNames.push_back("INSTALL");
-    targetNames.push_back("PACKAGE");
-    for (std::string const& t : targetNames) {
-      // check if target <t> 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_<t>_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[_<CONFIG>] 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";

+ 0 - 73
Source/cmGlobalVisualStudio7Generator.h

@@ -25,12 +25,6 @@ class cmake;
 template <typename T>
 class BT;
 
-struct cmVisualStudioFolder
-{
-  std::set<std::string> Projects;
-  std::set<std::string> SolutionItems;
-};
-
 /** \class cmGlobalVisualStudio7Generator
  * \brief Write a Unix makefiles.
  *
@@ -82,9 +76,6 @@ public:
     std::vector<std::string> const& makeOptions =
       std::vector<std::string>()) 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<std::string, cmVisualStudioFolder> 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<cmLocalGenerator*>& 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<std::string> const& configs,
-    std::set<std::string> 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<std::string> const& configs,
-    OrderedTargetDependSet const& projectTargets) const;
-
-  virtual void WriteExternalProject(
-    std::ostream& fout, std::string const& name, std::string const& path,
-    cmValue typeGuid,
-    std::set<BT<std::pair<std::string, bool>>> const& dependencies) const = 0;
-
-  std::string ConvertToSolutionPath(std::string const& path) const;
-
-  std::set<std::string> IsPartOfDefaultBuild(
-    std::vector<std::string> const& configs,
-    OrderedTargetDependSet const& projectTargets,
-    cmGeneratorTarget const* target) const;
-  bool IsDependedOn(OrderedTargetDependSet const& projectTargets,
-                    cmGeneratorTarget const* target) const;
-  std::map<std::string, std::string> 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;

+ 2 - 77
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<std::string> 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<std::string> const& configs,
-  std::set<std::string> 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)
 {

+ 5 - 20
Source/cmGlobalVisualStudio8Generator.h

@@ -9,7 +9,7 @@
 
 #include <cm/optional>
 
-#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<std::string> const& configs) const override;
-  void WriteProjectConfigurations(
-    std::ostream& fout, std::string const& name,
-    cmGeneratorTarget const& target, std::vector<std::string> const& configs,
-    std::set<std::string> 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;

+ 387 - 39
Source/cmGlobalVisualStudioGenerator.cxx

@@ -3,6 +3,7 @@
    file LICENSE.rst or https://cmake.org/licensing for details.  */
 #include "cmGlobalVisualStudioGenerator.h"
 
+#include <algorithm>
 #include <cassert>
 #include <future>
 #include <iostream>
@@ -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<std::string> cmGlobalVisualStudioGenerator::IsPartOfDefaultBuild(
+  std::vector<std::string> const& configs,
+  TargetDependSet const& projectTargets, cmGeneratorTarget const* target) const
+{
+  std::set<std::string> 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<std::string> targetNames;
+    targetNames.push_back("INSTALL");
+    targetNames.push_back("PACKAGE");
+    for (std::string const& t : targetNames) {
+      // check if target <t> 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_<t>_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[_<CONFIG>] 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<unsigned char> 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<std::string> 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<std::pair<std::string, bool>> 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<cmSourceGroup> sourceGroups = mf->GetSourceGroups();
+  std::vector<std::string> 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<std::string> 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<cmLocalGenerator*> 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);
+  }
+}

+ 31 - 11
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<std::string> IsPartOfDefaultBuild(
+    std::vector<std::string> const& configs,
+    TargetDependSet const& projectTargets,
+    cmGeneratorTarget const* target) const;
+  bool IsDependedOn(TargetDependSet const& projectTargets,
+                    cmGeneratorTarget const* target) const;
+  std::map<std::string, std::string> 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<cmLocalGenerator*> const& generators);
+
 private:
   virtual std::string GetVSMakeProgram() = 0;
   void PrintCompilerAdvice(std::ostream&, std::string const&,

+ 1 - 0
Source/cmGlobalVisualStudioVersionedGenerator.cxx

@@ -8,6 +8,7 @@
 #include <utility>
 #include <vector>
 
+#include <cm/optional>
 #include <cmext/string_view>
 
 #include "cmsys/FStream.hxx"

+ 1 - 0
Source/cmGlobalVisualStudioVersionedGenerator.h

@@ -8,6 +8,7 @@
 #include <string>
 
 #include <cm/optional>
+#include <cm/string_view>
 
 #include "cmGlobalVisualStudio10Generator.h"
 #include "cmGlobalVisualStudio14Generator.h"

+ 283 - 0
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 <algorithm>
+#include <cassert>
+#include <iostream>
+#include <map>
+
+#include <cm/string_view>
+#include <cmext/string_view>
+
+#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::Project const*> Solution::GetAllProjects() const
+{
+  std::vector<Project const*> 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 <typename T>
+T* GetEntry(std::map<cm::string_view, std::unique_ptr<T>>& entryMap,
+            cm::string_view name)
+{
+  auto i = entryMap.find(name);
+  if (i == entryMap.end()) {
+    auto p = cm::make_unique<T>();
+    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 <typename T>
+  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<Solution::Project const*> 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<Solution::Folder const*> 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<Solution::Project const*> 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";
+}
+
+}
+}

+ 169 - 0
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 <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <cm/memory>
+#include <cm/optional>
+#include <cm/string_view>
+
+#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<ProjectConfig> Configs;
+
+    /** Solution-level dependencies of the project on other projects.  */
+    std::vector<Project const*> 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<Folder const*> Folders;
+
+    /** List of projects contained inside this folder.  */
+    std::vector<Project const*> Projects;
+
+    /** Solution-level files contained inside this folder.  */
+    std::set<std::string> 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<std::string, std::string> Map;
+  };
+
+  /** Visual Studio major version number, if known.  */
+  cm::optional<Version> VSVersion;
+
+  /** Whether this is a VS Express edition, if known.  */
+  cm::optional<VersionExpress> 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<std::string> Configs;
+
+  /** List of all folders in the solution.  */
+  std::vector<Folder const*> Folders;
+
+  /** List of projects in the solution that are not in folders.  */
+  std::vector<Project const*> Projects;
+
+  /** List of solution-level property groups.  */
+  std::vector<PropertyGroup const*> PropertyGroups;
+
+  /** Name of the default startup project.  */
+  std::string StartupProject;
+
+  /** Get all projects in the solution, including all folders.  */
+  std::vector<Project const*> 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<cm::string_view, std::unique_ptr<Folder>> FolderMap;
+  std::map<cm::string_view, std::unique_ptr<Project>> ProjectMap;
+  std::map<cm::string_view, std::unique_ptr<PropertyGroup>> PropertyGroupMap;
+};
+
+/** Write the .sln-format representation.  */
+void WriteSln(std::ostream& sln, Solution const& solution);
+
+}
+}

+ 25 - 0
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 <cstdint>
+
+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,
+};
+}
+}

+ 0 - 2
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)

+ 1 - 1
Tests/RunCMake/VS10Project/VsDeployEnabled-check.cmake → 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()

+ 0 - 0
Tests/RunCMake/VS10Project/VsDeployEnabled.cmake → Tests/RunCMake/VSSolution/DeployEnabled.cmake


+ 2 - 0
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)

+ 0 - 0
Tests/RunCMake/VS10Project/SolutionItems-check.cmake → Tests/RunCMake/VSSolution/SolutionItems-check.cmake


+ 0 - 0
Tests/RunCMake/VS10Project/SolutionItems.cmake → Tests/RunCMake/VSSolution/SolutionItems.cmake


+ 0 - 0
Tests/RunCMake/VS10Project/SolutionItems/CMakeLists.txt → Tests/RunCMake/VSSolution/SolutionItems/CMakeLists.txt


+ 0 - 0
Tests/RunCMake/VS10Project/SolutionItems/extraneous.txt → Tests/RunCMake/VSSolution/SolutionItems/extraneous.txt


+ 0 - 0
Tests/RunCMake/VS10Project/solution-item-0-1.txt → Tests/RunCMake/VSSolution/foo.cpp


+ 0 - 0
Tests/RunCMake/VS10Project/solution-item-1-1.txt → Tests/RunCMake/VSSolution/solution-item-0-1.txt


+ 0 - 0
Tests/RunCMake/VS10Project/solution-item-2-1.txt → Tests/RunCMake/VSSolution/solution-item-1-1.txt


+ 0 - 0
Tests/RunCMake/VS10Project/solution-item-2-2.txt → Tests/RunCMake/VSSolution/solution-item-2-1.txt


+ 0 - 0
Tests/RunCMake/VSSolution/solution-item-2-2.txt