cmGlobalVisualStudioGenerator.cxx 39 KB

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