cmVSSolution.cxx 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file LICENSE.rst or https://cmake.org/licensing for details. */
  3. #include "cmVSSolution.h"
  4. #include <algorithm>
  5. #include <cassert>
  6. #include <iostream>
  7. #include <map>
  8. #include <cm/string_view>
  9. #include <cmext/string_view>
  10. #include "cmSystemTools.h"
  11. namespace cm {
  12. namespace VS {
  13. cm::string_view const Solution::Project::TypeIdDefault =
  14. "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942"_s;
  15. cm::string_view const Solution::Project::TypeIdCSharp =
  16. "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC"_s;
  17. cm::string_view const Solution::Project::TypeIdFortran =
  18. "6989167D-11E4-40FE-8C1A-2192A86A7E90"_s;
  19. cm::string_view const Solution::Folder::TypeId =
  20. "2150E333-8FDC-42A3-9474-1A3956D46DE8"_s;
  21. std::vector<Solution::Project const*> Solution::GetAllProjects() const
  22. {
  23. std::vector<Project const*> projects;
  24. projects.reserve(this->ProjectMap.size());
  25. for (Project const* project : this->Projects) {
  26. projects.emplace_back(project);
  27. }
  28. for (Folder const* folder : this->Folders) {
  29. for (Project const* project : folder->Projects) {
  30. projects.emplace_back(project);
  31. }
  32. }
  33. return projects;
  34. }
  35. namespace {
  36. template <typename T>
  37. T* GetEntry(std::map<cm::string_view, std::unique_ptr<T>>& entryMap,
  38. cm::string_view name)
  39. {
  40. auto i = entryMap.find(name);
  41. if (i == entryMap.end()) {
  42. auto p = cm::make_unique<T>();
  43. p->Name = name;
  44. i = entryMap.emplace(p->Name, std::move(p)).first;
  45. }
  46. return i->second.get();
  47. }
  48. }
  49. Solution::Folder* Solution::GetFolder(cm::string_view name)
  50. {
  51. return GetEntry(this->FolderMap, name);
  52. }
  53. Solution::Project* Solution::GetProject(cm::string_view name)
  54. {
  55. return GetEntry(this->ProjectMap, name);
  56. }
  57. Solution::PropertyGroup* Solution::GetPropertyGroup(cm::string_view name)
  58. {
  59. return GetEntry(this->PropertyGroupMap, name);
  60. }
  61. namespace {
  62. struct OrderByName
  63. {
  64. template <typename T>
  65. bool operator()(T const* l, T const* r) const
  66. {
  67. return l->Name < r->Name;
  68. }
  69. };
  70. }
  71. void Solution::CanonicalizeOrder()
  72. {
  73. std::sort(this->Folders.begin(), this->Folders.end(), OrderByName());
  74. for (auto& fi : this->FolderMap) {
  75. Folder* folder = fi.second.get();
  76. std::sort(folder->Folders.begin(), folder->Folders.end(), OrderByName());
  77. std::sort(folder->Projects.begin(), folder->Projects.end(), OrderByName());
  78. }
  79. std::sort(this->Projects.begin(), this->Projects.end(), OrderByName());
  80. for (auto& pi : this->ProjectMap) {
  81. Project* project = pi.second.get();
  82. std::sort(project->BuildDependencies.begin(),
  83. project->BuildDependencies.end(), OrderByName());
  84. }
  85. }
  86. namespace {
  87. void WriteSlnHeader(std::ostream& sln, Version version, VersionExpress express)
  88. {
  89. char utf8bom[] = { char(0xEF), char(0xBB), char(0xBF) };
  90. sln.write(utf8bom, 3);
  91. sln << '\n';
  92. switch (version) {
  93. case Version::VS14:
  94. // Visual Studio 14 writes .sln format 12.00
  95. sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
  96. if (express == VersionExpress::Yes) {
  97. sln << "# Visual Studio Express 14 for Windows Desktop\n";
  98. } else {
  99. sln << "# Visual Studio 14\n";
  100. }
  101. break;
  102. case Version::VS15:
  103. // Visual Studio 15 writes .sln format 12.00
  104. sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
  105. sln << "# Visual Studio 15\n";
  106. break;
  107. case Version::VS16:
  108. // Visual Studio 16 writes .sln format 12.00
  109. sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
  110. sln << "# Visual Studio Version 16\n";
  111. break;
  112. case Version::VS17:
  113. // Visual Studio 17 writes .sln format 12.00
  114. sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
  115. sln << "# Visual Studio Version 17\n";
  116. break;
  117. case Version::VS18:
  118. // Visual Studio 18 writes .sln format 12.00
  119. sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
  120. sln << "# Visual Studio Version 18\n";
  121. break;
  122. }
  123. }
  124. void WriteSlnProject(std::ostream& sln, Solution::Project const& project)
  125. {
  126. std::string projectPath = project.Path;
  127. std::replace(projectPath.begin(), projectPath.end(), '/', '\\');
  128. sln << "Project(\"{" << project.TypeId << "}\") = \"" << project.Name
  129. << "\", \"" << projectPath << "\", \"{" << project.Id << "}\"\n";
  130. sln << "\tProjectSection(ProjectDependencies) = postProject\n";
  131. for (Solution::Project const* d : project.BuildDependencies) {
  132. sln << "\t\t{" << d->Id << "} = {" << d->Id << "}\n";
  133. }
  134. sln << "\tEndProjectSection\n";
  135. sln << "EndProject\n";
  136. }
  137. void WriteSlnFolder(std::ostream& sln, Solution::Folder const& folder)
  138. {
  139. std::string folderName = folder.Name;
  140. std::replace(folderName.begin(), folderName.end(), '/', '\\');
  141. std::string const fileName = cmSystemTools::GetFilenameName(folder.Name);
  142. sln << "Project(\"{" << Solution::Folder::TypeId << "}\") = \"" << fileName
  143. << "\", \"" << folderName << "\", \"{" << folder.Id << "}\"\n";
  144. if (!folder.Files.empty()) {
  145. sln << "\tProjectSection(SolutionItems) = preProject\n";
  146. for (std::string const& item : folder.Files) {
  147. sln << "\t\t" << item << " = " << item << "\n";
  148. }
  149. sln << "\tEndProjectSection\n";
  150. }
  151. sln << "EndProject\n";
  152. }
  153. void WriteSlnSolutionConfigurationPlatforms(std::ostream& sln,
  154. Solution const& solution)
  155. {
  156. sln << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n";
  157. for (std::string const& config : solution.Configs) {
  158. sln << "\t\t" << config << '|' << solution.Platform << " = " << config
  159. << '|' << solution.Platform << '\n';
  160. }
  161. sln << "\tEndGlobalSection\n";
  162. }
  163. void WriteSlnProjectConfigurationPlatforms(std::ostream& sln,
  164. Solution const& solution,
  165. Solution::Project const& project)
  166. {
  167. auto const writeStep = [&sln, &solution, &project](std::size_t i,
  168. cm::string_view step) {
  169. sln << "\t\t{" << project.Id << "}." << solution.Configs[i] << '|'
  170. << solution.Platform << "." << step << " = "
  171. << project.Configs[i].Config << '|' << project.Platform << '\n';
  172. };
  173. assert(project.Configs.size() == solution.Configs.size());
  174. for (std::size_t i = 0; i < solution.Configs.size(); ++i) {
  175. writeStep(i, "ActiveCfg"_s);
  176. if (project.Configs[i].Build) {
  177. writeStep(i, "Build.0"_s);
  178. }
  179. if (project.Configs[i].Deploy) {
  180. writeStep(i, "Deploy.0"_s);
  181. }
  182. }
  183. }
  184. void WriteSlnProjectConfigurationPlatforms(
  185. std::ostream& sln, Solution const& solution,
  186. std::vector<Solution::Project const*> const& projects)
  187. {
  188. sln << "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n";
  189. for (Solution::Project const* project : projects) {
  190. WriteSlnProjectConfigurationPlatforms(sln, solution, *project);
  191. }
  192. sln << "\tEndGlobalSection\n";
  193. }
  194. void WriteSlnNestedProjects(
  195. std::ostream& sln, std::vector<Solution::Folder const*> const& folders)
  196. {
  197. sln << "\tGlobalSection(NestedProjects) = preSolution\n";
  198. for (Solution::Folder const* folder : folders) {
  199. for (Solution::Folder const* nestedFolder : folder->Folders) {
  200. sln << "\t\t{" << nestedFolder->Id << "} = {" << folder->Id << "}\n";
  201. }
  202. for (Solution::Project const* project : folder->Projects) {
  203. sln << "\t\t{" << project->Id << "} = {" << folder->Id << "}\n";
  204. }
  205. }
  206. sln << "\tEndGlobalSection\n";
  207. }
  208. void WriteSlnPropertyGroup(std::ostream& sln,
  209. Solution::PropertyGroup const& pg)
  210. {
  211. cm::string_view const order = pg.Scope == Solution::PropertyGroup::Load::Pre
  212. ? "preSolution"_s
  213. : "postSolution"_s;
  214. sln << "\tGlobalSection(" << pg.Name << ") = " << order << '\n';
  215. for (auto const& i : pg.Map) {
  216. sln << "\t\t" << i.first << " = " << i.second << '\n';
  217. }
  218. sln << "\tEndGlobalSection\n";
  219. }
  220. }
  221. void WriteSln(std::ostream& sln, Solution const& solution)
  222. {
  223. assert(solution.VSVersion);
  224. assert(solution.VSExpress);
  225. std::vector<Solution::Project const*> projects = solution.GetAllProjects();
  226. std::sort(projects.begin(), projects.end(),
  227. [&solution](Solution::Project const* l,
  228. Solution::Project const* r) -> bool {
  229. if (r->Name == solution.StartupProject) {
  230. return false;
  231. }
  232. if (l->Name == solution.StartupProject) {
  233. return true;
  234. }
  235. return l->Name < r->Name;
  236. });
  237. WriteSlnHeader(sln, *solution.VSVersion, *solution.VSExpress);
  238. for (Solution::Folder const* folder : solution.Folders) {
  239. WriteSlnFolder(sln, *folder);
  240. }
  241. for (Solution::Project const* project : projects) {
  242. WriteSlnProject(sln, *project);
  243. }
  244. sln << "Global\n";
  245. WriteSlnSolutionConfigurationPlatforms(sln, solution);
  246. WriteSlnProjectConfigurationPlatforms(sln, solution, projects);
  247. if (!solution.Folders.empty()) {
  248. WriteSlnNestedProjects(sln, solution.Folders);
  249. }
  250. for (Solution::PropertyGroup const* pg : solution.PropertyGroups) {
  251. WriteSlnPropertyGroup(sln, *pg);
  252. }
  253. sln << "EndGlobal\n";
  254. }
  255. }
  256. }