cmGlobalVisualStudioGenerator.cxx 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228
  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 "cmGlobalVisualStudioGenerator.h"
  4. #include <algorithm>
  5. #include <cassert>
  6. #include <future>
  7. #include <iostream>
  8. #include <sstream>
  9. #include <system_error>
  10. #include <utility>
  11. #include <cm/iterator>
  12. #include <cm/memory>
  13. #include <cmext/string_view>
  14. #include <windows.h>
  15. #include <objbase.h>
  16. #include <shellapi.h>
  17. #include "cmCallVisualStudioMacro.h"
  18. #include "cmCustomCommand.h"
  19. #include "cmCustomCommandLines.h"
  20. #include "cmGeneratedFileStream.h"
  21. #include "cmGeneratorTarget.h"
  22. #include "cmLocalGenerator.h"
  23. #include "cmMakefile.h"
  24. #include "cmMessageType.h"
  25. #include "cmPolicies.h"
  26. #include "cmSourceFile.h"
  27. #include "cmSourceGroup.h"
  28. #include "cmState.h"
  29. #include "cmStateTypes.h"
  30. #include "cmStringAlgorithms.h"
  31. #include "cmSystemTools.h"
  32. #include "cmTarget.h"
  33. #include "cmUuid.h"
  34. #include "cmake.h"
  35. cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator(cmake* cm)
  36. : cmGlobalGenerator(cm)
  37. {
  38. cm->GetState()->SetIsGeneratorMultiConfig(true);
  39. cm->GetState()->SetWindowsShell(true);
  40. cm->GetState()->SetWindowsVSIDE(true);
  41. this->DefaultPlatformName = "Win32";
  42. }
  43. cmGlobalVisualStudioGenerator::~cmGlobalVisualStudioGenerator() = default;
  44. cmGlobalVisualStudioGenerator::VSVersion
  45. cmGlobalVisualStudioGenerator::GetVersion() const
  46. {
  47. return this->Version;
  48. }
  49. void cmGlobalVisualStudioGenerator::SetVersion(VSVersion v)
  50. {
  51. this->Version = v;
  52. }
  53. void cmGlobalVisualStudioGenerator::EnableLanguage(
  54. std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
  55. {
  56. mf->AddDefinition("CMAKE_VS_PLATFORM_NAME_DEFAULT",
  57. this->DefaultPlatformName);
  58. this->cmGlobalGenerator::EnableLanguage(lang, mf, optional);
  59. }
  60. bool cmGlobalVisualStudioGenerator::SetGeneratorPlatform(std::string const& p,
  61. cmMakefile* mf)
  62. {
  63. if (!this->InitializePlatform(mf)) {
  64. return false;
  65. }
  66. if (this->GetPlatformName() == "x64"_s) {
  67. mf->AddDefinition("CMAKE_FORCE_WIN64", "TRUE");
  68. } else if (this->GetPlatformName() == "Itanium"_s) {
  69. mf->AddDefinition("CMAKE_FORCE_IA64", "TRUE");
  70. }
  71. mf->AddDefinition("CMAKE_VS_PLATFORM_NAME", this->GetPlatformName());
  72. return this->cmGlobalGenerator::SetGeneratorPlatform(p, mf);
  73. }
  74. bool cmGlobalVisualStudioGenerator::InitializePlatform(cmMakefile*)
  75. {
  76. return true;
  77. }
  78. cmValue cmGlobalVisualStudioGenerator::GetDebuggerWorkingDirectory(
  79. cmGeneratorTarget* gt) const
  80. {
  81. if (cmValue ret = gt->GetProperty("VS_DEBUGGER_WORKING_DIRECTORY")) {
  82. return ret;
  83. } else {
  84. return cmGlobalGenerator::GetDebuggerWorkingDirectory(gt);
  85. }
  86. }
  87. std::string const& cmGlobalVisualStudioGenerator::GetPlatformName() const
  88. {
  89. if (!this->GeneratorPlatform.empty()) {
  90. return this->GeneratorPlatform;
  91. }
  92. return this->DefaultPlatformName;
  93. }
  94. char const* cmGlobalVisualStudioGenerator::GetIDEVersion() const
  95. {
  96. switch (this->Version) {
  97. case cmGlobalVisualStudioGenerator::VSVersion::VS14:
  98. return "14.0";
  99. case cmGlobalVisualStudioGenerator::VSVersion::VS15:
  100. return "15.0";
  101. case cmGlobalVisualStudioGenerator::VSVersion::VS16:
  102. return "16.0";
  103. case cmGlobalVisualStudioGenerator::VSVersion::VS17:
  104. return "17.0";
  105. case cmGlobalVisualStudioGenerator::VSVersion::VS18:
  106. return "18.0";
  107. }
  108. return "";
  109. }
  110. std::string cmGlobalVisualStudioGenerator::GetRegistryBase()
  111. {
  112. return cmGlobalVisualStudioGenerator::GetRegistryBase(this->GetIDEVersion());
  113. }
  114. std::string cmGlobalVisualStudioGenerator::GetRegistryBase(char const* version)
  115. {
  116. return cmStrCat(R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\)",
  117. version);
  118. }
  119. void cmGlobalVisualStudioGenerator::AddExtraIDETargets()
  120. {
  121. // Add a special target that depends on ALL projects for easy build
  122. // of one configuration only.
  123. for (auto const& it : this->ProjectMap) {
  124. std::vector<cmLocalGenerator*> const& gen = it.second;
  125. // add the ALL_BUILD to the first local generator of each project
  126. if (!gen.empty()) {
  127. // Use no actual command lines so that the target itself is not
  128. // considered always out of date.
  129. auto cc = cm::make_unique<cmCustomCommand>();
  130. cc->SetEscapeOldStyle(false);
  131. cc->SetComment("Build all projects");
  132. cmTarget* allBuild =
  133. gen[0]->AddUtilityCommand("ALL_BUILD", true, std::move(cc));
  134. gen[0]->AddGeneratorTarget(
  135. cm::make_unique<cmGeneratorTarget>(allBuild, gen[0]));
  136. //
  137. // Organize in the "predefined targets" folder:
  138. //
  139. if (this->UseFolderProperty()) {
  140. allBuild->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
  141. }
  142. // Now make all targets depend on the ALL_BUILD target
  143. for (cmLocalGenerator const* i : gen) {
  144. for (auto const& tgt : i->GetGeneratorTargets()) {
  145. if (tgt->GetType() == cmStateEnums::GLOBAL_TARGET ||
  146. tgt->IsImported()) {
  147. continue;
  148. }
  149. if (!this->IsExcluded(gen[0], tgt.get())) {
  150. allBuild->AddUtility(tgt->GetName(), false);
  151. }
  152. }
  153. }
  154. }
  155. }
  156. // Configure CMake Visual Studio macros, for this user on this version
  157. // of Visual Studio.
  158. this->ConfigureCMakeVisualStudioMacros();
  159. }
  160. void cmGlobalVisualStudioGenerator::ComputeTargetObjectDirectory(
  161. cmGeneratorTarget* gt) const
  162. {
  163. std::string dir =
  164. cmStrCat(gt->GetSupportDirectory(), '/', this->GetCMakeCFGIntDir(), '/');
  165. gt->ObjectDirectory = dir;
  166. }
  167. bool IsVisualStudioMacrosFileRegistered(std::string const& macrosFile,
  168. std::string const& regKeyBase,
  169. std::string& nextAvailableSubKeyName);
  170. void RegisterVisualStudioMacros(std::string const& macrosFile,
  171. std::string const& regKeyBase);
  172. #define CMAKE_VSMACROS_FILENAME "CMakeVSMacros2.vsmacros"
  173. #define CMAKE_VSMACROS_RELOAD_MACRONAME \
  174. "Macros.CMakeVSMacros2.Macros.ReloadProjects"
  175. #define CMAKE_VSMACROS_STOP_MACRONAME "Macros.CMakeVSMacros2.Macros.StopBuild"
  176. void cmGlobalVisualStudioGenerator::ConfigureCMakeVisualStudioMacros()
  177. {
  178. std::string dir = this->GetUserMacrosDirectory();
  179. if (!dir.empty()) {
  180. std::string src = cmStrCat(cmSystemTools::GetCMakeRoot(),
  181. "/Templates/" CMAKE_VSMACROS_FILENAME);
  182. std::string dst = cmStrCat(dir, "/CMakeMacros/" CMAKE_VSMACROS_FILENAME);
  183. // Copy the macros file to the user directory only if the
  184. // destination does not exist or the source location is newer.
  185. // This will allow the user to edit the macros for development
  186. // purposes but newer versions distributed with CMake will replace
  187. // older versions in user directories.
  188. int res;
  189. if (!cmSystemTools::FileTimeCompare(src, dst, &res) || res > 0) {
  190. if (!cmSystemTools::CopyFileAlways(src, dst)) {
  191. std::ostringstream oss;
  192. oss << "Could not copy from: " << src << std::endl
  193. << " to: " << dst << std::endl;
  194. cmSystemTools::Message(oss.str(), "Warning");
  195. }
  196. }
  197. RegisterVisualStudioMacros(dst, this->GetUserMacrosRegKeyBase());
  198. }
  199. }
  200. void cmGlobalVisualStudioGenerator::CallVisualStudioMacro(
  201. MacroName m, std::string const& vsSolutionFile)
  202. {
  203. // If any solution or project files changed during the generation,
  204. // tell Visual Studio to reload them...
  205. std::string dir = this->GetUserMacrosDirectory();
  206. // Only really try to call the macro if:
  207. // - there is a UserMacrosDirectory
  208. // - the CMake vsmacros file exists
  209. // - the CMake vsmacros file is registered
  210. // - there were .sln/.vcproj files changed during generation
  211. //
  212. if (!dir.empty()) {
  213. std::string macrosFile =
  214. cmStrCat(dir, "/CMakeMacros/" CMAKE_VSMACROS_FILENAME);
  215. std::string nextSubkeyName;
  216. if (cmSystemTools::FileExists(macrosFile) &&
  217. IsVisualStudioMacrosFileRegistered(
  218. macrosFile, this->GetUserMacrosRegKeyBase(), nextSubkeyName)) {
  219. if (m == MacroReload) {
  220. std::vector<std::string> filenames;
  221. this->GetFilesReplacedDuringGenerate(filenames);
  222. if (!filenames.empty()) {
  223. std::string projects = cmJoin(filenames, ";");
  224. cmCallVisualStudioMacro::CallMacro(
  225. vsSolutionFile, CMAKE_VSMACROS_RELOAD_MACRONAME, projects,
  226. this->GetCMakeInstance()->GetDebugOutput());
  227. }
  228. } else if (m == MacroStop) {
  229. cmCallVisualStudioMacro::CallMacro(
  230. vsSolutionFile, CMAKE_VSMACROS_STOP_MACRONAME, "",
  231. this->GetCMakeInstance()->GetDebugOutput());
  232. }
  233. }
  234. }
  235. }
  236. std::string cmGlobalVisualStudioGenerator::GetUserMacrosDirectory()
  237. {
  238. return "";
  239. }
  240. std::string cmGlobalVisualStudioGenerator::GetUserMacrosRegKeyBase()
  241. {
  242. return "";
  243. }
  244. bool cmGlobalVisualStudioGenerator::FindMakeProgram(cmMakefile* mf)
  245. {
  246. // Visual Studio generators know how to lookup their build tool
  247. // directly instead of needing a helper module to do it, so we
  248. // do not actually need to put CMAKE_MAKE_PROGRAM into the cache.
  249. if (mf->GetDefinition("CMAKE_MAKE_PROGRAM").IsOff()) {
  250. mf->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetVSMakeProgram());
  251. }
  252. return true;
  253. }
  254. std::string cmGlobalVisualStudioGenerator::GetStartupProjectName(
  255. cmLocalGenerator const* root) const
  256. {
  257. cmValue n = root->GetMakefile()->GetProperty("VS_STARTUP_PROJECT");
  258. if (cmNonempty(n)) {
  259. std::string startup = *n;
  260. if (this->FindTarget(startup)) {
  261. return startup;
  262. }
  263. root->GetMakefile()->IssueMessage(
  264. MessageType::AUTHOR_WARNING,
  265. cmStrCat("Directory property VS_STARTUP_PROJECT specifies target "
  266. "'",
  267. startup, "' that does not exist. Ignoring."));
  268. }
  269. // default, if not specified
  270. return this->GetAllTargetName();
  271. }
  272. bool IsVisualStudioMacrosFileRegistered(std::string const& macrosFile,
  273. std::string const& regKeyBase,
  274. std::string& nextAvailableSubKeyName)
  275. {
  276. bool macrosRegistered = false;
  277. std::string s1;
  278. std::string s2;
  279. // Make lowercase local copies, convert to Unix slashes, and
  280. // see if the resulting strings are the same:
  281. s1 = cmSystemTools::LowerCase(macrosFile);
  282. cmSystemTools::ConvertToUnixSlashes(s1);
  283. std::string keyname;
  284. HKEY hkey = nullptr;
  285. LONG result = ERROR_SUCCESS;
  286. DWORD index = 0;
  287. keyname = cmStrCat(regKeyBase, "\\OtherProjects7");
  288. hkey = nullptr;
  289. result =
  290. RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
  291. 0, KEY_READ, &hkey);
  292. if (ERROR_SUCCESS == result) {
  293. // Iterate the subkeys and look for the values of interest in each subkey:
  294. wchar_t subkeyname[256];
  295. DWORD cch_subkeyname = cm::size(subkeyname);
  296. wchar_t keyclass[256];
  297. DWORD cch_keyclass = cm::size(keyclass);
  298. FILETIME lastWriteTime;
  299. lastWriteTime.dwHighDateTime = 0;
  300. lastWriteTime.dwLowDateTime = 0;
  301. while (ERROR_SUCCESS ==
  302. RegEnumKeyExW(hkey, index, subkeyname, &cch_subkeyname, 0, keyclass,
  303. &cch_keyclass, &lastWriteTime)) {
  304. // Open the subkey and query the values of interest:
  305. HKEY hsubkey = nullptr;
  306. result = RegOpenKeyExW(hkey, subkeyname, 0, KEY_READ, &hsubkey);
  307. if (ERROR_SUCCESS == result) {
  308. DWORD valueType = REG_SZ;
  309. wchar_t data1[256];
  310. DWORD cch_data1 = sizeof(data1);
  311. RegQueryValueExW(hsubkey, L"Path", 0, &valueType, (LPBYTE)data1,
  312. &cch_data1);
  313. DWORD data2 = 0;
  314. DWORD cch_data2 = sizeof(data2);
  315. RegQueryValueExW(hsubkey, L"Security", 0, &valueType, (LPBYTE)&data2,
  316. &cch_data2);
  317. DWORD data3 = 0;
  318. DWORD cch_data3 = sizeof(data3);
  319. RegQueryValueExW(hsubkey, L"StorageFormat", 0, &valueType,
  320. (LPBYTE)&data3, &cch_data3);
  321. s2 = cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1));
  322. cmSystemTools::ConvertToUnixSlashes(s2);
  323. if (s2 == s1) {
  324. macrosRegistered = true;
  325. }
  326. std::string fullname = cmsys::Encoding::ToNarrow(data1);
  327. std::string filename;
  328. std::string filepath;
  329. std::string filepathname;
  330. std::string filepathpath;
  331. if (cmSystemTools::FileExists(fullname)) {
  332. filename = cmSystemTools::GetFilenameName(fullname);
  333. filepath = cmSystemTools::GetFilenamePath(fullname);
  334. filepathname = cmSystemTools::GetFilenameName(filepath);
  335. filepathpath = cmSystemTools::GetFilenamePath(filepath);
  336. }
  337. // std::cout << keyname << "\\" << subkeyname << ":" << std::endl;
  338. // std::cout << " Path: " << data1 << std::endl;
  339. // std::cout << " Security: " << data2 << std::endl;
  340. // std::cout << " StorageFormat: " << data3 << std::endl;
  341. // std::cout << " filename: " << filename << std::endl;
  342. // std::cout << " filepath: " << filepath << std::endl;
  343. // std::cout << " filepathname: " << filepathname << std::endl;
  344. // std::cout << " filepathpath: " << filepathpath << std::endl;
  345. // std::cout << std::endl;
  346. RegCloseKey(hsubkey);
  347. } else {
  348. std::cout << "error opening subkey: "
  349. << cmsys::Encoding::ToNarrow(subkeyname) << std::endl;
  350. std::cout << std::endl;
  351. }
  352. ++index;
  353. cch_subkeyname = cm::size(subkeyname);
  354. cch_keyclass = cm::size(keyclass);
  355. lastWriteTime.dwHighDateTime = 0;
  356. lastWriteTime.dwLowDateTime = 0;
  357. }
  358. RegCloseKey(hkey);
  359. } else {
  360. std::cout << "error opening key: " << keyname << std::endl;
  361. std::cout << std::endl;
  362. }
  363. // Pass back next available sub key name, assuming sub keys always
  364. // follow the expected naming scheme. Expected naming scheme is that
  365. // the subkeys of OtherProjects7 is 0 to n-1, so it's ok to use "n"
  366. // as the name of the next subkey.
  367. nextAvailableSubKeyName = std::to_string(index);
  368. keyname = cmStrCat(regKeyBase, "\\RecordingProject7");
  369. hkey = nullptr;
  370. result =
  371. RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
  372. 0, KEY_READ, &hkey);
  373. if (ERROR_SUCCESS == result) {
  374. DWORD valueType = REG_SZ;
  375. wchar_t data1[256];
  376. DWORD cch_data1 = sizeof(data1);
  377. RegQueryValueExW(hkey, L"Path", 0, &valueType, (LPBYTE)data1, &cch_data1);
  378. DWORD data2 = 0;
  379. DWORD cch_data2 = sizeof(data2);
  380. RegQueryValueExW(hkey, L"Security", 0, &valueType, (LPBYTE)&data2,
  381. &cch_data2);
  382. DWORD data3 = 0;
  383. DWORD cch_data3 = sizeof(data3);
  384. RegQueryValueExW(hkey, L"StorageFormat", 0, &valueType, (LPBYTE)&data3,
  385. &cch_data3);
  386. s2 = cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1));
  387. cmSystemTools::ConvertToUnixSlashes(s2);
  388. if (s2 == s1) {
  389. macrosRegistered = true;
  390. }
  391. // std::cout << keyname << ":" << std::endl;
  392. // std::cout << " Path: " << data1 << std::endl;
  393. // std::cout << " Security: " << data2 << std::endl;
  394. // std::cout << " StorageFormat: " << data3 << std::endl;
  395. // std::cout << std::endl;
  396. RegCloseKey(hkey);
  397. } else {
  398. std::cout << "error opening key: " << keyname << std::endl;
  399. std::cout << std::endl;
  400. }
  401. return macrosRegistered;
  402. }
  403. void WriteVSMacrosFileRegistryEntry(std::string const& nextAvailableSubKeyName,
  404. std::string const& macrosFile,
  405. std::string const& regKeyBase)
  406. {
  407. std::string keyname = cmStrCat(regKeyBase, "\\OtherProjects7");
  408. HKEY hkey = nullptr;
  409. LONG result =
  410. RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
  411. 0, KEY_READ | KEY_WRITE, &hkey);
  412. if (ERROR_SUCCESS == result) {
  413. // Create the subkey and set the values of interest:
  414. HKEY hsubkey = nullptr;
  415. wchar_t lpClass[] = L"";
  416. result = RegCreateKeyExW(
  417. hkey, cmsys::Encoding::ToWide(nextAvailableSubKeyName).c_str(), 0,
  418. lpClass, 0, KEY_READ | KEY_WRITE, 0, &hsubkey, 0);
  419. if (ERROR_SUCCESS == result) {
  420. DWORD dw = 0;
  421. std::string s(macrosFile);
  422. std::replace(s.begin(), s.end(), '/', '\\');
  423. std::wstring ws = cmsys::Encoding::ToWide(s);
  424. result =
  425. RegSetValueExW(hsubkey, L"Path", 0, REG_SZ, (LPBYTE)ws.c_str(),
  426. static_cast<DWORD>(ws.size() + 1) * sizeof(wchar_t));
  427. if (ERROR_SUCCESS != result) {
  428. std::cout << "error result 1: " << result << std::endl;
  429. std::cout << std::endl;
  430. }
  431. // Security value is always "1" for sample macros files (seems to be "2"
  432. // if you put the file somewhere outside the standard VSMacros folder)
  433. dw = 1;
  434. result = RegSetValueExW(hsubkey, L"Security", 0, REG_DWORD, (LPBYTE)&dw,
  435. sizeof(DWORD));
  436. if (ERROR_SUCCESS != result) {
  437. std::cout << "error result 2: " << result << std::endl;
  438. std::cout << std::endl;
  439. }
  440. // StorageFormat value is always "0" for sample macros files
  441. dw = 0;
  442. result = RegSetValueExW(hsubkey, L"StorageFormat", 0, REG_DWORD,
  443. (LPBYTE)&dw, sizeof(DWORD));
  444. if (ERROR_SUCCESS != result) {
  445. std::cout << "error result 3: " << result << std::endl;
  446. std::cout << std::endl;
  447. }
  448. RegCloseKey(hsubkey);
  449. } else {
  450. std::cout << "error creating subkey: " << nextAvailableSubKeyName
  451. << std::endl;
  452. std::cout << std::endl;
  453. }
  454. RegCloseKey(hkey);
  455. } else {
  456. std::cout << "error opening key: " << keyname << std::endl;
  457. std::cout << std::endl;
  458. }
  459. }
  460. void RegisterVisualStudioMacros(std::string const& macrosFile,
  461. std::string const& regKeyBase)
  462. {
  463. bool macrosRegistered;
  464. std::string nextAvailableSubKeyName;
  465. macrosRegistered = IsVisualStudioMacrosFileRegistered(
  466. macrosFile, regKeyBase, nextAvailableSubKeyName);
  467. if (!macrosRegistered) {
  468. int count =
  469. cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances("ALL");
  470. // Only register the macros file if there are *no* instances of Visual
  471. // Studio running. If we register it while one is running, first, it has
  472. // no effect on the running instance; second, and worse, Visual Studio
  473. // removes our newly added registration entry when it quits. Instead,
  474. // emit a warning asking the user to exit all running Visual Studio
  475. // instances...
  476. //
  477. if (0 != count) {
  478. std::ostringstream oss;
  479. oss << "Could not register CMake's Visual Studio macros file '"
  480. << CMAKE_VSMACROS_FILENAME "' while Visual Studio is running."
  481. << " Please exit all running instances of Visual Studio before"
  482. << " continuing." << std::endl
  483. << std::endl
  484. << "CMake needs to register Visual Studio macros when its macros"
  485. << " file is updated or when it detects that its current macros file"
  486. << " is no longer registered with Visual Studio." << std::endl;
  487. cmSystemTools::Message(oss.str(), "Warning");
  488. // Count them again now that the warning is over. In the case of a GUI
  489. // warning, the user may have gone to close Visual Studio and then come
  490. // back to the CMake GUI and clicked ok on the above warning. If so,
  491. // then register the macros *now* if the count is *now* 0...
  492. //
  493. count = cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
  494. "ALL");
  495. // Also re-get the nextAvailableSubKeyName in case Visual Studio
  496. // wrote out new registered macros information as it was exiting:
  497. //
  498. if (0 == count) {
  499. IsVisualStudioMacrosFileRegistered(macrosFile, regKeyBase,
  500. nextAvailableSubKeyName);
  501. }
  502. }
  503. // Do another if check - 'count' may have changed inside the above if:
  504. //
  505. if (0 == count) {
  506. WriteVSMacrosFileRegistryEntry(nextAvailableSubKeyName, macrosFile,
  507. regKeyBase);
  508. }
  509. }
  510. }
  511. bool cmGlobalVisualStudioGenerator::TargetIsFortranOnly(
  512. cmGeneratorTarget const* gt) const
  513. {
  514. // If there's only one source language, Fortran has to be used
  515. // in order for the sources to compile.
  516. std::set<std::string> languages = gt->GetAllConfigCompileLanguages();
  517. // Consider an explicit linker language property, but *not* the
  518. // computed linker language that may depend on linked targets.
  519. // This allows the project to control the language choice in
  520. // a target with none of its own sources, e.g. when also using
  521. // object libraries.
  522. cmValue linkLang = gt->GetProperty("LINKER_LANGUAGE");
  523. if (cmNonempty(linkLang)) {
  524. languages.insert(*linkLang);
  525. }
  526. // Intel Fortran .vfproj files do support the resource compiler.
  527. languages.erase("RC");
  528. return languages.size() == 1 && *languages.begin() == "Fortran"_s;
  529. }
  530. bool cmGlobalVisualStudioGenerator::IsInSolution(
  531. cmGeneratorTarget const* gt) const
  532. {
  533. return gt->IsInBuildSystem();
  534. }
  535. bool cmGlobalVisualStudioGenerator::IsDepInSolution(
  536. std::string const& targetName) const
  537. {
  538. return !targetName.empty();
  539. }
  540. bool cmGlobalVisualStudioGenerator::TargetCompare::operator()(
  541. cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
  542. {
  543. // Make sure a given named target is ordered first,
  544. // e.g. to set ALL_BUILD as the default active project.
  545. // When the empty string is named this is a no-op.
  546. if (r->GetName() == this->First) {
  547. return false;
  548. }
  549. if (l->GetName() == this->First) {
  550. return true;
  551. }
  552. return l->GetName() < r->GetName();
  553. }
  554. cmGlobalVisualStudioGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
  555. TargetDependSet const& targets, std::string const& first)
  556. : derived(TargetCompare(first))
  557. {
  558. this->insert(targets.begin(), targets.end());
  559. }
  560. cmGlobalVisualStudioGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
  561. TargetSet const& targets, std::string const& first)
  562. : derived(TargetCompare(first))
  563. {
  564. for (cmGeneratorTarget const* it : targets) {
  565. this->insert(it);
  566. }
  567. }
  568. std::string cmGlobalVisualStudioGenerator::ExpandCFGIntDir(
  569. std::string const& str, std::string const& config) const
  570. {
  571. std::string replace = GetCMakeCFGIntDir();
  572. std::string tmp = str;
  573. for (std::string::size_type i = tmp.find(replace); i != std::string::npos;
  574. i = tmp.find(replace, i)) {
  575. tmp.replace(i, replace.size(), config);
  576. i += config.size();
  577. }
  578. return tmp;
  579. }
  580. void cmGlobalVisualStudioGenerator::AddSymbolExportCommand(
  581. cmGeneratorTarget* gt, std::vector<cmCustomCommand>& commands,
  582. std::string const& configName)
  583. {
  584. cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
  585. gt->GetModuleDefinitionInfo(configName);
  586. if (!mdi || !mdi->DefFileGenerated) {
  587. return;
  588. }
  589. std::vector<std::string> outputs;
  590. outputs.push_back(mdi->DefFile);
  591. std::vector<std::string> empty;
  592. std::vector<cmSourceFile const*> objectSources;
  593. gt->GetObjectSources(objectSources, configName);
  594. std::map<cmSourceFile const*, cmObjectLocations> mapping;
  595. for (cmSourceFile const* it : objectSources) {
  596. mapping[it];
  597. }
  598. gt->LocalGenerator->ComputeObjectFilenames(mapping, configName, gt);
  599. std::string obj_dir = gt->ObjectDirectory;
  600. std::string cmakeCommand = cmSystemTools::GetCMakeCommand();
  601. std::string obj_dir_expanded = obj_dir;
  602. cmSystemTools::ReplaceString(obj_dir_expanded, this->GetCMakeCFGIntDir(),
  603. configName.c_str());
  604. cmSystemTools::MakeDirectory(obj_dir_expanded);
  605. std::string const objs_file = cmStrCat(obj_dir_expanded, "/objects.txt");
  606. cmGeneratedFileStream fout(objs_file.c_str());
  607. if (!fout) {
  608. cmSystemTools::Error(cmStrCat("could not open ", objs_file));
  609. return;
  610. }
  611. auto const useShortPaths = this->UseShortObjectNames()
  612. ? cmObjectLocations::UseShortPath::Yes
  613. : cmObjectLocations::UseShortPath::No;
  614. if (mdi->WindowsExportAllSymbols) {
  615. std::vector<std::string> objs;
  616. for (cmSourceFile const* it : objectSources) {
  617. // Find the object file name corresponding to this source file.
  618. // It must exist because we populated the mapping just above.
  619. auto const& locs = mapping[it];
  620. std::string const& v = locs.GetPath(useShortPaths);
  621. assert(!v.empty());
  622. std::string objFile = cmStrCat(obj_dir, v);
  623. objs.push_back(objFile);
  624. }
  625. std::vector<cmSourceFile const*> externalObjectSources;
  626. gt->GetExternalObjects(externalObjectSources, configName);
  627. for (cmSourceFile const* it : externalObjectSources) {
  628. objs.push_back(it->GetFullPath());
  629. }
  630. for (std::string const& it : objs) {
  631. std::string objFile = it;
  632. // replace $(ConfigurationName) in the object names
  633. cmSystemTools::ReplaceString(objFile, this->GetCMakeCFGIntDir(),
  634. configName);
  635. if (cmHasLiteralSuffix(objFile, ".obj")) {
  636. fout << objFile << "\n";
  637. }
  638. }
  639. }
  640. for (cmSourceFile const* i : mdi->Sources) {
  641. fout << i->GetFullPath() << "\n";
  642. }
  643. cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
  644. { cmakeCommand, "-E", "__create_def", mdi->DefFile, objs_file });
  645. cmCustomCommand command;
  646. command.SetOutputs(outputs);
  647. command.SetCommandLines(commandLines);
  648. command.SetComment("Auto build dll exports");
  649. command.SetBacktrace(gt->Target->GetMakefile()->GetBacktrace());
  650. command.SetWorkingDirectory(".");
  651. command.SetStdPipesUTF8(true);
  652. commands.push_back(std::move(command));
  653. }
  654. static bool OpenSolution(std::string const& sln)
  655. {
  656. HRESULT comInitialized =
  657. CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
  658. if (FAILED(comInitialized)) {
  659. return false;
  660. }
  661. HINSTANCE hi = ShellExecuteA(nullptr, "open", sln.c_str(), nullptr, nullptr,
  662. SW_SHOWNORMAL);
  663. CoUninitialize();
  664. return reinterpret_cast<intptr_t>(hi) > 32;
  665. }
  666. bool cmGlobalVisualStudioGenerator::Open(std::string const& bindir,
  667. std::string const& projectName,
  668. bool dryRun)
  669. {
  670. std::string sln = this->GetSLNFile(bindir, projectName);
  671. if (dryRun) {
  672. return cmSystemTools::FileExists(sln, true);
  673. }
  674. sln = cmSystemTools::ConvertToOutputPath(sln);
  675. return std::async(std::launch::async, OpenSolution, sln).get();
  676. }
  677. cm::string_view cmGlobalVisualStudioGenerator::ExternalProjectTypeId(
  678. std::string const& path)
  679. {
  680. using namespace cm::VS;
  681. std::string const extension = cmSystemTools::GetFilenameLastExtension(path);
  682. if (extension == ".vfproj"_s) {
  683. return Solution::Project::TypeIdFortran;
  684. }
  685. if (extension == ".vbproj"_s) {
  686. return Solution::Project::TypeIdVisualBasic;
  687. }
  688. if (extension == ".csproj"_s) {
  689. return Solution::Project::TypeIdCSharp;
  690. }
  691. if (extension == ".fsproj"_s) {
  692. return Solution::Project::TypeIdFSharp;
  693. }
  694. if (extension == ".vdproj"_s) {
  695. return Solution::Project::TypeIdVDProj;
  696. }
  697. if (extension == ".dbproj"_s) {
  698. return Solution::Project::TypeIdDatabase;
  699. }
  700. if (extension == ".wapproj"_s) {
  701. return Solution::Project::TypeIdWinAppPkg;
  702. }
  703. if (extension == ".wixproj"_s) {
  704. return Solution::Project::TypeIdWiX;
  705. }
  706. if (extension == ".pyproj"_s) {
  707. return Solution::Project::TypeIdPython;
  708. }
  709. return Solution::Project::TypeIdDefault;
  710. }
  711. bool cmGlobalVisualStudioGenerator::IsDependedOn(
  712. TargetDependSet const& projectTargets, cmGeneratorTarget const* gtIn) const
  713. {
  714. return std::any_of(projectTargets.begin(), projectTargets.end(),
  715. [this, gtIn](cmTargetDepend const& l) {
  716. TargetDependSet const& tgtdeps =
  717. this->GetTargetDirectDepends(l);
  718. return tgtdeps.count(gtIn);
  719. });
  720. }
  721. std::set<std::string> cmGlobalVisualStudioGenerator::IsPartOfDefaultBuild(
  722. std::vector<std::string> const& configs,
  723. TargetDependSet const& projectTargets, cmGeneratorTarget const* target) const
  724. {
  725. std::set<std::string> activeConfigs;
  726. // if it is a utility target then only make it part of the
  727. // default build if another target depends on it
  728. int type = target->GetType();
  729. if (type == cmStateEnums::GLOBAL_TARGET) {
  730. std::vector<std::string> targetNames;
  731. targetNames.push_back("INSTALL");
  732. targetNames.push_back("PACKAGE");
  733. for (std::string const& t : targetNames) {
  734. // check if target <t> is part of default build
  735. if (target->GetName() == t) {
  736. std::string const propertyName =
  737. cmStrCat("CMAKE_VS_INCLUDE_", t, "_TO_DEFAULT_BUILD");
  738. // inspect CMAKE_VS_INCLUDE_<t>_TO_DEFAULT_BUILD properties
  739. for (std::string const& i : configs) {
  740. cmValue propertyValue =
  741. target->Target->GetMakefile()->GetDefinition(propertyName);
  742. if (propertyValue &&
  743. cmIsOn(cmGeneratorExpression::Evaluate(
  744. *propertyValue, target->GetLocalGenerator(), i))) {
  745. activeConfigs.insert(i);
  746. }
  747. }
  748. }
  749. }
  750. return activeConfigs;
  751. }
  752. if (type == cmStateEnums::UTILITY &&
  753. !this->IsDependedOn(projectTargets, target)) {
  754. return activeConfigs;
  755. }
  756. // inspect EXCLUDE_FROM_DEFAULT_BUILD[_<CONFIG>] properties
  757. for (std::string const& i : configs) {
  758. if (target->GetFeature("EXCLUDE_FROM_DEFAULT_BUILD", i).IsOff()) {
  759. activeConfigs.insert(i);
  760. }
  761. }
  762. return activeConfigs;
  763. }
  764. std::string cmGlobalVisualStudioGenerator::GetGUID(
  765. std::string const& name) const
  766. {
  767. std::string const& guidStoreName = cmStrCat(name, "_GUID_CMAKE");
  768. if (cmValue storedGUID =
  769. this->CMakeInstance->GetCacheDefinition(guidStoreName)) {
  770. return *storedGUID;
  771. }
  772. // Compute a GUID that is deterministic but unique to the build tree.
  773. std::string input =
  774. cmStrCat(this->CMakeInstance->GetState()->GetBinaryDirectory(), '|', name);
  775. cmUuid uuidGenerator;
  776. std::vector<unsigned char> uuidNamespace;
  777. uuidGenerator.StringToBinary("ee30c4be-5192-4fb0-b335-722a2dffe760",
  778. uuidNamespace);
  779. std::string guid = uuidGenerator.FromMd5(uuidNamespace, input);
  780. return cmSystemTools::UpperCase(guid);
  781. }
  782. cm::VS::Solution::Folder* cmGlobalVisualStudioGenerator::CreateSolutionFolder(
  783. cm::VS::Solution& solution, cm::string_view rawName) const
  784. {
  785. cm::VS::Solution::Folder* folder = nullptr;
  786. std::string canonicalName;
  787. for (std::string::size_type cur = 0;;) {
  788. static std::string delims = "/\\";
  789. cur = rawName.find_first_not_of(delims, cur);
  790. if (cur == std::string::npos) {
  791. break;
  792. }
  793. std::string::size_type end = rawName.find_first_of(delims, cur);
  794. cm::string_view f = end == std::string::npos
  795. ? rawName.substr(cur)
  796. : rawName.substr(cur, end - cur);
  797. canonicalName =
  798. canonicalName.empty() ? std::string(f) : cmStrCat(canonicalName, '/', f);
  799. cm::VS::Solution::Folder* nextFolder = solution.GetFolder(canonicalName);
  800. if (nextFolder->Id.empty()) {
  801. nextFolder->Id =
  802. this->GetGUID(cmStrCat("CMAKE_FOLDER_GUID_"_s, canonicalName));
  803. if (folder) {
  804. folder->Folders.emplace_back(nextFolder);
  805. }
  806. solution.Folders.emplace_back(nextFolder);
  807. }
  808. folder = nextFolder;
  809. cur = end;
  810. }
  811. return folder;
  812. }
  813. cm::VS::Solution cmGlobalVisualStudioGenerator::CreateSolution(
  814. cmLocalGenerator const* root, TargetDependSet const& projectTargets) const
  815. {
  816. using namespace cm::VS;
  817. Solution solution;
  818. solution.VSVersion = this->Version;
  819. solution.VSExpress =
  820. this->ExpressEdition ? VersionExpress::Yes : VersionExpress::No;
  821. solution.Platform = this->GetPlatformName();
  822. solution.Configs =
  823. root->GetMakefile()->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
  824. solution.StartupProject = this->GetStartupProjectName(root);
  825. auto addProject = [this, useFolders = this->UseFolderProperty(),
  826. &solution](cmGeneratorTarget const* gt,
  827. Solution::Project const* p) {
  828. if (Solution::Folder* const folder = useFolders
  829. ? this->CreateSolutionFolder(solution, gt->GetEffectiveFolderName())
  830. : nullptr) {
  831. folder->Projects.emplace_back(p);
  832. } else {
  833. solution.Projects.emplace_back(p);
  834. }
  835. };
  836. for (cmTargetDepend const& projectTarget : projectTargets) {
  837. cmGeneratorTarget const* gt = projectTarget;
  838. if (!this->IsInSolution(gt)) {
  839. continue;
  840. }
  841. Solution::Project* project = solution.GetProject(gt->GetName());
  842. project->Id = this->GetGUID(gt->GetName());
  843. std::set<std::string> const& includeConfigs =
  844. this->IsPartOfDefaultBuild(solution.Configs, projectTargets, gt);
  845. auto addProjectConfig =
  846. [this, project, gt, &includeConfigs](std::string const& solutionConfig,
  847. std::string const& projectConfig) {
  848. bool const build =
  849. includeConfigs.find(solutionConfig) != includeConfigs.end();
  850. bool const deploy = this->NeedsDeploy(*gt, solutionConfig.c_str());
  851. project->Configs.emplace_back(
  852. Solution::ProjectConfig{ projectConfig, build, deploy });
  853. };
  854. if (cmValue expath = gt->GetProperty("EXTERNAL_MSPROJECT")) {
  855. project->Path = *expath;
  856. cmValue const projectType = gt->GetProperty("VS_PROJECT_TYPE");
  857. if (!projectType.IsEmpty()) {
  858. project->TypeId = *projectType;
  859. } else {
  860. project->TypeId = this->ExternalProjectTypeId(project->Path);
  861. }
  862. for (std::string const& config : solution.Configs) {
  863. cmList mapConfig{ gt->GetProperty(cmStrCat(
  864. "MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(config))) };
  865. addProjectConfig(config, !mapConfig.empty() ? mapConfig[0] : config);
  866. }
  867. cmValue platformMapping = gt->GetProperty("VS_PLATFORM_MAPPING");
  868. project->Platform =
  869. !platformMapping.IsEmpty() ? *platformMapping : solution.Platform;
  870. for (BT<std::pair<std::string, bool>> const& i : gt->GetUtilities()) {
  871. std::string const& dep = i.Value.first;
  872. if (this->IsDepInSolution(dep)) {
  873. project->BuildDependencies.emplace_back(solution.GetProject(dep));
  874. }
  875. }
  876. addProject(gt, project);
  877. continue;
  878. }
  879. if (cmValue vcprojName = gt->GetProperty("GENERATOR_FILE_NAME")) {
  880. cmLocalGenerator* lg = gt->GetLocalGenerator();
  881. std::string dir =
  882. root->MaybeRelativeToCurBinDir(lg->GetCurrentBinaryDirectory());
  883. if (dir == "."_s) {
  884. dir.clear();
  885. } else if (!cmHasSuffix(dir, '/')) {
  886. dir += "/";
  887. }
  888. cm::string_view vcprojExt;
  889. if (this->TargetIsFortranOnly(gt)) {
  890. vcprojExt = ".vfproj"_s;
  891. project->TypeId = Solution::Project::TypeIdFortran;
  892. } else if (gt->IsCSharpOnly()) {
  893. vcprojExt = ".csproj"_s;
  894. project->TypeId = Solution::Project::TypeIdCSharp;
  895. } else {
  896. vcprojExt = ".vcproj"_s;
  897. project->TypeId = Solution::Project::TypeIdDefault;
  898. }
  899. if (cmValue genExt = gt->GetProperty("GENERATOR_FILE_NAME_EXT")) {
  900. vcprojExt = *genExt;
  901. }
  902. project->Path = cmStrCat(dir, *vcprojName, vcprojExt);
  903. if (gt->IsDotNetSdkTarget() &&
  904. !cmGlobalVisualStudioGenerator::IsReservedTarget(gt->GetName())) {
  905. cmValue platformTarget = gt->GetProperty("VS_GLOBAL_PlatformTarget");
  906. if (!platformTarget.IsEmpty()) {
  907. project->Platform = *platformTarget;
  908. } else {
  909. project->Platform =
  910. // On VS 16 and above, always map .NET SDK projects to "Any CPU".
  911. this->Version >= VSVersion::VS16 ? "Any CPU" : solution.Platform;
  912. }
  913. } else {
  914. project->Platform = solution.Platform;
  915. }
  916. // Add solution-level dependencies.
  917. TargetDependSet const& depends = this->GetTargetDirectDepends(gt);
  918. for (cmTargetDepend const& dep : depends) {
  919. if (this->IsInSolution(dep)) {
  920. project->BuildDependencies.emplace_back(
  921. solution.GetProject(dep->GetName()));
  922. }
  923. }
  924. for (std::string const& config : solution.Configs) {
  925. addProjectConfig(config, config);
  926. }
  927. addProject(gt, project);
  928. continue;
  929. }
  930. }
  931. cmMakefile* mf = root->GetMakefile();
  932. // Unfortunately we have to copy the source groups because
  933. // FindSourceGroup uses a regex which is modifying the group.
  934. std::vector<cmSourceGroup> sourceGroups = mf->GetSourceGroups();
  935. std::vector<std::string> items =
  936. cmList{ root->GetMakefile()->GetProperty("VS_SOLUTION_ITEMS") };
  937. for (std::string item : items) {
  938. if (!cmSystemTools::FileIsFullPath(item)) {
  939. item =
  940. cmSystemTools::CollapseFullPath(item, mf->GetCurrentSourceDirectory());
  941. }
  942. cmSourceGroup* sg = mf->FindSourceGroup(item, sourceGroups);
  943. std::string folderName = sg->GetFullName();
  944. if (folderName.empty()) {
  945. folderName = "Solution Items"_s;
  946. }
  947. Solution::Folder* folder =
  948. this->CreateSolutionFolder(solution, folderName);
  949. folder->Files.emplace(std::move(item));
  950. }
  951. Solution::PropertyGroup* pgExtensibilityGlobals = nullptr;
  952. Solution::PropertyGroup* pgExtensibilityAddIns = nullptr;
  953. std::vector<std::string> const propKeys =
  954. root->GetMakefile()->GetPropertyKeys();
  955. for (std::string const& it : propKeys) {
  956. if (!cmHasLiteralPrefix(it, "VS_GLOBAL_SECTION_")) {
  957. continue;
  958. }
  959. std::string name = it.substr(18);
  960. Solution::PropertyGroup::Load scope;
  961. if (cmHasLiteralPrefix(name, "PRE_")) {
  962. name = name.substr(4);
  963. scope = Solution::PropertyGroup::Load::Pre;
  964. } else if (cmHasLiteralPrefix(name, "POST_")) {
  965. name = name.substr(5);
  966. scope = Solution::PropertyGroup::Load::Post;
  967. } else {
  968. continue;
  969. }
  970. if (name.empty()) {
  971. continue;
  972. }
  973. Solution::PropertyGroup* pg = solution.GetPropertyGroup(name);
  974. solution.PropertyGroups.emplace_back(pg);
  975. pg->Scope = scope;
  976. cmList keyValuePairs{ root->GetMakefile()->GetProperty(it) };
  977. for (std::string const& itPair : keyValuePairs) {
  978. std::string::size_type const posEqual = itPair.find('=');
  979. if (posEqual != std::string::npos) {
  980. std::string key = cmTrimWhitespace(itPair.substr(0, posEqual));
  981. std::string value = cmTrimWhitespace(itPair.substr(posEqual + 1));
  982. pg->Map.emplace(std::move(key), std::move(value));
  983. }
  984. }
  985. if (name == "ExtensibilityGlobals"_s) {
  986. pgExtensibilityGlobals = pg;
  987. } else if (name == "ExtensibilityAddIns"_s) {
  988. pgExtensibilityAddIns = pg;
  989. }
  990. }
  991. if (this->Version <= cm::VS::Version::VS17) {
  992. if (!pgExtensibilityGlobals) {
  993. pgExtensibilityGlobals =
  994. solution.GetPropertyGroup("ExtensibilityGlobals"_s);
  995. solution.PropertyGroups.emplace_back(pgExtensibilityGlobals);
  996. }
  997. std::string const solutionGuid =
  998. this->GetGUID(cmStrCat(root->GetProjectName(), ".sln"));
  999. pgExtensibilityGlobals->Map.emplace("SolutionGuid",
  1000. cmStrCat('{', solutionGuid, '}'));
  1001. if (!pgExtensibilityAddIns) {
  1002. pgExtensibilityAddIns =
  1003. solution.GetPropertyGroup("ExtensibilityAddIns"_s);
  1004. solution.PropertyGroups.emplace_back(pgExtensibilityAddIns);
  1005. }
  1006. }
  1007. solution.CanonicalizeOrder();
  1008. return solution;
  1009. }
  1010. std::string cmGlobalVisualStudioGenerator::GetSLNFile(
  1011. cmLocalGenerator const* root) const
  1012. {
  1013. return this->GetSLNFile(root->GetCurrentBinaryDirectory(),
  1014. root->GetProjectName());
  1015. }
  1016. std::string cmGlobalVisualStudioGenerator::GetSLNFile(
  1017. std::string const& projectDir, std::string const& projectName) const
  1018. {
  1019. std::string slnFile = projectDir;
  1020. if (!slnFile.empty()) {
  1021. slnFile.push_back('/');
  1022. }
  1023. slnFile = cmStrCat(slnFile, projectName, ".sln");
  1024. if (this->Version >= cm::VS::Version::VS18) {
  1025. slnFile += "x";
  1026. }
  1027. return slnFile;
  1028. }
  1029. void cmGlobalVisualStudioGenerator::Generate()
  1030. {
  1031. // first do the superclass method
  1032. this->cmGlobalGenerator::Generate();
  1033. // Now write out the VS Solution files.
  1034. for (auto& it : this->ProjectMap) {
  1035. this->GenerateSolution(it.second[0], it.second);
  1036. }
  1037. // If any solution or project files changed during the generation,
  1038. // tell Visual Studio to reload them...
  1039. if (!cmSystemTools::GetErrorOccurredFlag() &&
  1040. !this->LocalGenerators.empty()) {
  1041. this->CallVisualStudioMacro(MacroReload,
  1042. GetSLNFile(this->LocalGenerators[0].get()));
  1043. }
  1044. if (this->Version == VSVersion::VS14 &&
  1045. !this->CMakeInstance->GetIsInTryCompile()) {
  1046. std::string cmakeWarnVS14;
  1047. if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue(
  1048. "CMAKE_WARN_VS14")) {
  1049. this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS14");
  1050. cmakeWarnVS14 = *cached;
  1051. } else {
  1052. cmSystemTools::GetEnv("CMAKE_WARN_VS14", cmakeWarnVS14);
  1053. }
  1054. if (cmakeWarnVS14.empty() || !cmIsOff(cmakeWarnVS14)) {
  1055. this->CMakeInstance->IssueMessage(
  1056. MessageType::WARNING,
  1057. "The \"Visual Studio 14 2015\" generator is deprecated "
  1058. "and will be removed in a future version of CMake."
  1059. "\n"
  1060. "Add CMAKE_WARN_VS14=OFF to the cache to disable this warning.");
  1061. }
  1062. }
  1063. }
  1064. void cmGlobalVisualStudioGenerator::GenerateSolution(
  1065. cmLocalGenerator const* root,
  1066. std::vector<cmLocalGenerator*> const& generators)
  1067. {
  1068. if (generators.empty()) {
  1069. return;
  1070. }
  1071. // Collect all targets under this root generator and the transitive
  1072. // closure of their dependencies.
  1073. TargetDependSet const projectTargets =
  1074. this->GetTargetsForProject(root, generators);
  1075. std::string fname = GetSLNFile(root);
  1076. cmGeneratedFileStream fout(fname);
  1077. fout.SetCopyIfDifferent(true);
  1078. if (!fout) {
  1079. return;
  1080. }
  1081. cm::VS::Solution const solution = this->CreateSolution(root, projectTargets);
  1082. if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS18) {
  1083. WriteSlnx(fout, solution);
  1084. } else {
  1085. WriteSln(fout, solution);
  1086. }
  1087. if (fout.Close()) {
  1088. this->FileReplacedDuringGenerate(fname);
  1089. }
  1090. }