cmGlobalVisualStudioVersionedGenerator.cxx 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmGlobalVisualStudioVersionedGenerator.h"
  4. #include <cstring>
  5. #include <set>
  6. #include <sstream>
  7. #include <utility>
  8. #include <vector>
  9. #include <cmext/string_view>
  10. #include "cmsys/FStream.hxx"
  11. #include "cmsys/Glob.hxx"
  12. #include "cmsys/RegularExpression.hxx"
  13. #include "cmDocumentationEntry.h"
  14. #include "cmGlobalGenerator.h"
  15. #include "cmGlobalGeneratorFactory.h"
  16. #include "cmMakefile.h"
  17. #include "cmMessageType.h"
  18. #include "cmStateTypes.h"
  19. #include "cmStringAlgorithms.h"
  20. #include "cmSystemTools.h"
  21. #include "cmVSSetupHelper.h"
  22. #include "cmake.h"
  23. #if defined(_M_ARM64)
  24. # define HOST_PLATFORM_NAME "ARM64"
  25. # define HOST_TOOLS_ARCH ""
  26. #elif defined(_M_ARM)
  27. # define HOST_PLATFORM_NAME "ARM"
  28. # define HOST_TOOLS_ARCH ""
  29. #elif defined(_M_IA64)
  30. # define HOST_PLATFORM_NAME "Itanium"
  31. # define HOST_TOOLS_ARCH ""
  32. #elif defined(_WIN64)
  33. # define HOST_PLATFORM_NAME "x64"
  34. # define HOST_TOOLS_ARCH "x64"
  35. #else
  36. static bool VSIsWow64()
  37. {
  38. BOOL isWow64 = false;
  39. return IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64;
  40. }
  41. #endif
  42. static std::string VSHostPlatformName()
  43. {
  44. #ifdef HOST_PLATFORM_NAME
  45. return HOST_PLATFORM_NAME;
  46. #else
  47. if (VSIsWow64()) {
  48. return "x64";
  49. } else {
  50. return "Win32";
  51. }
  52. #endif
  53. }
  54. static std::string VSHostArchitecture()
  55. {
  56. #ifdef HOST_TOOLS_ARCH
  57. return HOST_TOOLS_ARCH;
  58. #else
  59. if (VSIsWow64()) {
  60. return "x64";
  61. } else {
  62. return "x86";
  63. }
  64. #endif
  65. }
  66. static unsigned int VSVersionToMajor(
  67. cmGlobalVisualStudioGenerator::VSVersion v)
  68. {
  69. switch (v) {
  70. case cmGlobalVisualStudioGenerator::VSVersion::VS9:
  71. return 9;
  72. case cmGlobalVisualStudioGenerator::VSVersion::VS10:
  73. return 10;
  74. case cmGlobalVisualStudioGenerator::VSVersion::VS11:
  75. return 11;
  76. case cmGlobalVisualStudioGenerator::VSVersion::VS12:
  77. return 12;
  78. case cmGlobalVisualStudioGenerator::VSVersion::VS14:
  79. return 14;
  80. case cmGlobalVisualStudioGenerator::VSVersion::VS15:
  81. return 15;
  82. case cmGlobalVisualStudioGenerator::VSVersion::VS16:
  83. return 16;
  84. case cmGlobalVisualStudioGenerator::VSVersion::VS17:
  85. return 17;
  86. }
  87. return 0;
  88. }
  89. static const char* VSVersionToToolset(
  90. cmGlobalVisualStudioGenerator::VSVersion v)
  91. {
  92. switch (v) {
  93. case cmGlobalVisualStudioGenerator::VSVersion::VS9:
  94. return "v90";
  95. case cmGlobalVisualStudioGenerator::VSVersion::VS10:
  96. return "v100";
  97. case cmGlobalVisualStudioGenerator::VSVersion::VS11:
  98. return "v110";
  99. case cmGlobalVisualStudioGenerator::VSVersion::VS12:
  100. return "v120";
  101. case cmGlobalVisualStudioGenerator::VSVersion::VS14:
  102. return "v140";
  103. case cmGlobalVisualStudioGenerator::VSVersion::VS15:
  104. return "v141";
  105. case cmGlobalVisualStudioGenerator::VSVersion::VS16:
  106. return "v142";
  107. case cmGlobalVisualStudioGenerator::VSVersion::VS17:
  108. return "v143";
  109. }
  110. return "";
  111. }
  112. static std::string VSVersionToMajorString(
  113. cmGlobalVisualStudioGenerator::VSVersion v)
  114. {
  115. switch (v) {
  116. case cmGlobalVisualStudioGenerator::VSVersion::VS9:
  117. return "9";
  118. case cmGlobalVisualStudioGenerator::VSVersion::VS10:
  119. return "10";
  120. case cmGlobalVisualStudioGenerator::VSVersion::VS11:
  121. return "11";
  122. case cmGlobalVisualStudioGenerator::VSVersion::VS12:
  123. return "12";
  124. case cmGlobalVisualStudioGenerator::VSVersion::VS14:
  125. return "14";
  126. case cmGlobalVisualStudioGenerator::VSVersion::VS15:
  127. return "15";
  128. case cmGlobalVisualStudioGenerator::VSVersion::VS16:
  129. return "16";
  130. case cmGlobalVisualStudioGenerator::VSVersion::VS17:
  131. return "17";
  132. }
  133. return "";
  134. }
  135. static const char* VSVersionToAndroidToolset(
  136. cmGlobalVisualStudioGenerator::VSVersion v)
  137. {
  138. switch (v) {
  139. case cmGlobalVisualStudioGenerator::VSVersion::VS9:
  140. case cmGlobalVisualStudioGenerator::VSVersion::VS10:
  141. case cmGlobalVisualStudioGenerator::VSVersion::VS11:
  142. case cmGlobalVisualStudioGenerator::VSVersion::VS12:
  143. return "";
  144. case cmGlobalVisualStudioGenerator::VSVersion::VS14:
  145. return "Clang_3_8";
  146. case cmGlobalVisualStudioGenerator::VSVersion::VS15:
  147. case cmGlobalVisualStudioGenerator::VSVersion::VS16:
  148. case cmGlobalVisualStudioGenerator::VSVersion::VS17:
  149. return "Clang_5_0";
  150. }
  151. return "";
  152. }
  153. static const char vs15generatorName[] = "Visual Studio 15 2017";
  154. // Map generator name without year to name with year.
  155. static const char* cmVS15GenName(const std::string& name, std::string& genName)
  156. {
  157. if (strncmp(name.c_str(), vs15generatorName,
  158. sizeof(vs15generatorName) - 6) != 0) {
  159. return 0;
  160. }
  161. const char* p = name.c_str() + sizeof(vs15generatorName) - 6;
  162. if (cmHasLiteralPrefix(p, " 2017")) {
  163. p += 5;
  164. }
  165. genName = std::string(vs15generatorName) + p;
  166. return p;
  167. }
  168. class cmGlobalVisualStudioVersionedGenerator::Factory15
  169. : public cmGlobalGeneratorFactory
  170. {
  171. public:
  172. std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
  173. const std::string& name, bool allowArch, cmake* cm) const override
  174. {
  175. std::string genName;
  176. const char* p = cmVS15GenName(name, genName);
  177. if (!p) {
  178. return std::unique_ptr<cmGlobalGenerator>();
  179. }
  180. if (!*p) {
  181. return std::unique_ptr<cmGlobalGenerator>(
  182. new cmGlobalVisualStudioVersionedGenerator(
  183. cmGlobalVisualStudioGenerator::VSVersion::VS15, cm, genName, ""));
  184. }
  185. if (!allowArch || *p++ != ' ') {
  186. return std::unique_ptr<cmGlobalGenerator>();
  187. }
  188. if (strcmp(p, "Win64") == 0) {
  189. return std::unique_ptr<cmGlobalGenerator>(
  190. new cmGlobalVisualStudioVersionedGenerator(
  191. cmGlobalVisualStudioGenerator::VSVersion::VS15, cm, genName, "x64"));
  192. }
  193. if (strcmp(p, "ARM") == 0) {
  194. return std::unique_ptr<cmGlobalGenerator>(
  195. new cmGlobalVisualStudioVersionedGenerator(
  196. cmGlobalVisualStudioGenerator::VSVersion::VS15, cm, genName, "ARM"));
  197. }
  198. return std::unique_ptr<cmGlobalGenerator>();
  199. }
  200. void GetDocumentation(cmDocumentationEntry& entry) const override
  201. {
  202. entry.Name = std::string(vs15generatorName) + " [arch]";
  203. entry.Brief = "Generates Visual Studio 2017 project files. "
  204. "Optional [arch] can be \"Win64\" or \"ARM\".";
  205. }
  206. std::vector<std::string> GetGeneratorNames() const override
  207. {
  208. std::vector<std::string> names;
  209. names.push_back(vs15generatorName);
  210. return names;
  211. }
  212. std::vector<std::string> GetGeneratorNamesWithPlatform() const override
  213. {
  214. std::vector<std::string> names;
  215. names.push_back(vs15generatorName + std::string(" ARM"));
  216. names.push_back(vs15generatorName + std::string(" Win64"));
  217. return names;
  218. }
  219. bool SupportsToolset() const override { return true; }
  220. bool SupportsPlatform() const override { return true; }
  221. std::vector<std::string> GetKnownPlatforms() const override
  222. {
  223. std::vector<std::string> platforms;
  224. platforms.emplace_back("x64");
  225. platforms.emplace_back("Win32");
  226. platforms.emplace_back("ARM");
  227. platforms.emplace_back("ARM64");
  228. return platforms;
  229. }
  230. std::string GetDefaultPlatformName() const override { return "Win32"; }
  231. };
  232. std::unique_ptr<cmGlobalGeneratorFactory>
  233. cmGlobalVisualStudioVersionedGenerator::NewFactory15()
  234. {
  235. return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory15);
  236. }
  237. static const char vs16generatorName[] = "Visual Studio 16 2019";
  238. static const char vs17generatorName[] = "Visual Studio 17 2022";
  239. // Map generator name without year to name with year.
  240. static const char* cmVS16GenName(const std::string& name, std::string& genName)
  241. {
  242. if (strncmp(name.c_str(), vs16generatorName,
  243. sizeof(vs16generatorName) - 6) != 0) {
  244. return 0;
  245. }
  246. const char* p = name.c_str() + sizeof(vs16generatorName) - 6;
  247. if (cmHasLiteralPrefix(p, " 2019")) {
  248. p += 5;
  249. }
  250. genName = std::string(vs16generatorName) + p;
  251. return p;
  252. }
  253. static const char* cmVS17GenName(const std::string& name, std::string& genName)
  254. {
  255. if (strncmp(name.c_str(), vs17generatorName,
  256. sizeof(vs17generatorName) - 6) != 0) {
  257. return 0;
  258. }
  259. const char* p = name.c_str() + sizeof(vs17generatorName) - 6;
  260. if (cmHasLiteralPrefix(p, " 2022")) {
  261. p += 5;
  262. }
  263. genName = std::string(vs17generatorName) + p;
  264. return p;
  265. }
  266. class cmGlobalVisualStudioVersionedGenerator::Factory16
  267. : public cmGlobalGeneratorFactory
  268. {
  269. public:
  270. std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
  271. const std::string& name, bool /*allowArch*/, cmake* cm) const override
  272. {
  273. std::string genName;
  274. const char* p = cmVS16GenName(name, genName);
  275. if (!p) {
  276. return std::unique_ptr<cmGlobalGenerator>();
  277. }
  278. if (!*p) {
  279. return std::unique_ptr<cmGlobalGenerator>(
  280. new cmGlobalVisualStudioVersionedGenerator(
  281. cmGlobalVisualStudioGenerator::VSVersion::VS16, cm, genName, ""));
  282. }
  283. return std::unique_ptr<cmGlobalGenerator>();
  284. }
  285. void GetDocumentation(cmDocumentationEntry& entry) const override
  286. {
  287. entry.Name = std::string(vs16generatorName);
  288. entry.Brief = "Generates Visual Studio 2019 project files. "
  289. "Use -A option to specify architecture.";
  290. }
  291. std::vector<std::string> GetGeneratorNames() const override
  292. {
  293. std::vector<std::string> names;
  294. names.push_back(vs16generatorName);
  295. return names;
  296. }
  297. std::vector<std::string> GetGeneratorNamesWithPlatform() const override
  298. {
  299. return std::vector<std::string>();
  300. }
  301. bool SupportsToolset() const override { return true; }
  302. bool SupportsPlatform() const override { return true; }
  303. std::vector<std::string> GetKnownPlatforms() const override
  304. {
  305. std::vector<std::string> platforms;
  306. platforms.emplace_back("x64");
  307. platforms.emplace_back("Win32");
  308. platforms.emplace_back("ARM");
  309. platforms.emplace_back("ARM64");
  310. platforms.emplace_back("ARM64EC");
  311. return platforms;
  312. }
  313. std::string GetDefaultPlatformName() const override
  314. {
  315. return VSHostPlatformName();
  316. }
  317. };
  318. std::unique_ptr<cmGlobalGeneratorFactory>
  319. cmGlobalVisualStudioVersionedGenerator::NewFactory16()
  320. {
  321. return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory16);
  322. }
  323. class cmGlobalVisualStudioVersionedGenerator::Factory17
  324. : public cmGlobalGeneratorFactory
  325. {
  326. public:
  327. std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
  328. const std::string& name, bool /*allowArch*/, cmake* cm) const override
  329. {
  330. std::string genName;
  331. const char* p = cmVS17GenName(name, genName);
  332. if (!p) {
  333. return std::unique_ptr<cmGlobalGenerator>();
  334. }
  335. if (!*p) {
  336. return std::unique_ptr<cmGlobalGenerator>(
  337. new cmGlobalVisualStudioVersionedGenerator(
  338. cmGlobalVisualStudioGenerator::VSVersion::VS17, cm, genName, ""));
  339. }
  340. return std::unique_ptr<cmGlobalGenerator>();
  341. }
  342. void GetDocumentation(cmDocumentationEntry& entry) const override
  343. {
  344. entry.Name = std::string(vs17generatorName);
  345. entry.Brief = "Generates Visual Studio 2022 project files. "
  346. "Use -A option to specify architecture.";
  347. }
  348. std::vector<std::string> GetGeneratorNames() const override
  349. {
  350. std::vector<std::string> names;
  351. names.push_back(vs17generatorName);
  352. return names;
  353. }
  354. std::vector<std::string> GetGeneratorNamesWithPlatform() const override
  355. {
  356. return std::vector<std::string>();
  357. }
  358. bool SupportsToolset() const override { return true; }
  359. bool SupportsPlatform() const override { return true; }
  360. std::vector<std::string> GetKnownPlatforms() const override
  361. {
  362. std::vector<std::string> platforms;
  363. platforms.emplace_back("x64");
  364. platforms.emplace_back("Win32");
  365. platforms.emplace_back("ARM");
  366. platforms.emplace_back("ARM64");
  367. platforms.emplace_back("ARM64EC");
  368. return platforms;
  369. }
  370. std::string GetDefaultPlatformName() const override
  371. {
  372. return VSHostPlatformName();
  373. }
  374. };
  375. std::unique_ptr<cmGlobalGeneratorFactory>
  376. cmGlobalVisualStudioVersionedGenerator::NewFactory17()
  377. {
  378. return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory17);
  379. }
  380. cmGlobalVisualStudioVersionedGenerator::cmGlobalVisualStudioVersionedGenerator(
  381. VSVersion version, cmake* cm, const std::string& name,
  382. std::string const& platformInGeneratorName)
  383. : cmGlobalVisualStudio14Generator(cm, name, platformInGeneratorName)
  384. , vsSetupAPIHelper(VSVersionToMajor(version))
  385. {
  386. this->Version = version;
  387. this->ExpressEdition = false;
  388. this->DefaultPlatformToolset = VSVersionToToolset(this->Version);
  389. this->DefaultAndroidToolset = VSVersionToAndroidToolset(this->Version);
  390. this->DefaultCLFlagTableName = VSVersionToToolset(this->Version);
  391. this->DefaultCSharpFlagTableName = VSVersionToToolset(this->Version);
  392. this->DefaultLinkFlagTableName = VSVersionToToolset(this->Version);
  393. if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16) {
  394. this->DefaultPlatformName = VSHostPlatformName();
  395. this->DefaultPlatformToolsetHostArchitecture = VSHostArchitecture();
  396. }
  397. if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS17) {
  398. // FIXME: Search for an existing framework? Under '%ProgramFiles(x86)%',
  399. // see 'Reference Assemblies\Microsoft\Framework\.NETFramework'.
  400. // Use a version installed by VS 2022 without a separate component.
  401. this->DefaultTargetFrameworkVersion = "v4.7.2";
  402. }
  403. }
  404. bool cmGlobalVisualStudioVersionedGenerator::MatchesGeneratorName(
  405. const std::string& name) const
  406. {
  407. std::string genName;
  408. switch (this->Version) {
  409. case cmGlobalVisualStudioGenerator::VSVersion::VS9:
  410. case cmGlobalVisualStudioGenerator::VSVersion::VS10:
  411. case cmGlobalVisualStudioGenerator::VSVersion::VS11:
  412. case cmGlobalVisualStudioGenerator::VSVersion::VS12:
  413. case cmGlobalVisualStudioGenerator::VSVersion::VS14:
  414. break;
  415. case cmGlobalVisualStudioGenerator::VSVersion::VS15:
  416. if (cmVS15GenName(name, genName)) {
  417. return genName == this->GetName();
  418. }
  419. break;
  420. case cmGlobalVisualStudioGenerator::VSVersion::VS16:
  421. if (cmVS16GenName(name, genName)) {
  422. return genName == this->GetName();
  423. }
  424. break;
  425. case cmGlobalVisualStudioGenerator::VSVersion::VS17:
  426. if (cmVS17GenName(name, genName)) {
  427. return genName == this->GetName();
  428. }
  429. break;
  430. }
  431. return false;
  432. }
  433. bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
  434. std::string const& i, cmMakefile* mf)
  435. {
  436. if (this->LastGeneratorInstanceString &&
  437. i == *(this->LastGeneratorInstanceString)) {
  438. return true;
  439. }
  440. if (!this->ParseGeneratorInstance(i, mf)) {
  441. return false;
  442. }
  443. if (!this->GeneratorInstanceVersion.empty()) {
  444. std::string const majorStr = VSVersionToMajorString(this->Version);
  445. cmsys::RegularExpression versionRegex(
  446. cmStrCat("^", majorStr, "\\.[0-9]+\\.[0-9]+\\.[0-9]+$"));
  447. if (!versionRegex.find(this->GeneratorInstanceVersion)) {
  448. std::ostringstream e;
  449. /* clang-format off */
  450. e <<
  451. "Generator\n"
  452. " " << this->GetName() << "\n"
  453. "given instance specification\n"
  454. " " << i << "\n"
  455. "but the version field is not 4 integer components"
  456. " starting in " << majorStr << "."
  457. ;
  458. /* clang-format on */
  459. mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
  460. return false;
  461. }
  462. }
  463. std::string vsInstance;
  464. if (!i.empty()) {
  465. vsInstance = i;
  466. if (!this->vsSetupAPIHelper.SetVSInstance(
  467. this->GeneratorInstance, this->GeneratorInstanceVersion)) {
  468. std::ostringstream e;
  469. /* clang-format off */
  470. e <<
  471. "Generator\n"
  472. " " << this->GetName() << "\n"
  473. "could not find specified instance of Visual Studio:\n"
  474. " " << i;
  475. /* clang-format on */
  476. if (!this->GeneratorInstance.empty() &&
  477. this->GeneratorInstanceVersion.empty() &&
  478. cmSystemTools::FileIsDirectory(this->GeneratorInstance)) {
  479. e << "\n"
  480. "The directory exists, but the instance is not known to the "
  481. "Visual Studio Installer, and no 'version=' field was given.";
  482. }
  483. mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
  484. return false;
  485. }
  486. } else if (!this->vsSetupAPIHelper.GetVSInstanceInfo(vsInstance)) {
  487. std::ostringstream e;
  488. /* clang-format off */
  489. e <<
  490. "Generator\n"
  491. " " << this->GetName() << "\n"
  492. "could not find any instance of Visual Studio.\n";
  493. /* clang-format on */
  494. mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
  495. return false;
  496. }
  497. // Save the selected instance persistently.
  498. std::string genInstance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
  499. if (vsInstance != genInstance) {
  500. this->CMakeInstance->AddCacheEntry("CMAKE_GENERATOR_INSTANCE", vsInstance,
  501. "Generator instance identifier.",
  502. cmStateEnums::INTERNAL);
  503. }
  504. // The selected instance may have a different MSBuild than previously found.
  505. this->MSBuildCommandInitialized = false;
  506. this->LastGeneratorInstanceString = i;
  507. return true;
  508. }
  509. bool cmGlobalVisualStudioVersionedGenerator::ParseGeneratorInstance(
  510. std::string const& is, cmMakefile* mf)
  511. {
  512. this->GeneratorInstance.clear();
  513. this->GeneratorInstanceVersion.clear();
  514. std::vector<std::string> const fields = cmTokenize(is, ",");
  515. std::vector<std::string>::const_iterator fi = fields.begin();
  516. if (fi == fields.end()) {
  517. return true;
  518. }
  519. // The first field may be the VS instance.
  520. if (fi->find('=') == fi->npos) {
  521. this->GeneratorInstance = *fi;
  522. ++fi;
  523. }
  524. std::set<std::string> handled;
  525. // The rest of the fields must be key=value pairs.
  526. for (; fi != fields.end(); ++fi) {
  527. std::string::size_type pos = fi->find('=');
  528. if (pos == fi->npos) {
  529. std::ostringstream e;
  530. /* clang-format off */
  531. e <<
  532. "Generator\n"
  533. " " << this->GetName() << "\n"
  534. "given instance specification\n"
  535. " " << is << "\n"
  536. "that contains a field after the first ',' with no '='."
  537. ;
  538. /* clang-format on */
  539. mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
  540. return false;
  541. }
  542. std::string const key = fi->substr(0, pos);
  543. std::string const value = fi->substr(pos + 1);
  544. if (!handled.insert(key).second) {
  545. std::ostringstream e;
  546. /* clang-format off */
  547. e <<
  548. "Generator\n"
  549. " " << this->GetName() << "\n"
  550. "given instance specification\n"
  551. " " << is << "\n"
  552. "that contains duplicate field key '" << key << "'."
  553. ;
  554. /* clang-format on */
  555. mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
  556. return false;
  557. }
  558. if (!this->ProcessGeneratorInstanceField(key, value)) {
  559. std::ostringstream e;
  560. /* clang-format off */
  561. e <<
  562. "Generator\n"
  563. " " << this->GetName() << "\n"
  564. "given instance specification\n"
  565. " " << is << "\n"
  566. "that contains invalid field '" << *fi << "'."
  567. ;
  568. /* clang-format on */
  569. mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
  570. return false;
  571. }
  572. }
  573. return true;
  574. }
  575. bool cmGlobalVisualStudioVersionedGenerator::ProcessGeneratorInstanceField(
  576. std::string const& key, std::string const& value)
  577. {
  578. if (key == "version") {
  579. this->GeneratorInstanceVersion = value;
  580. return true;
  581. }
  582. return false;
  583. }
  584. bool cmGlobalVisualStudioVersionedGenerator::GetVSInstance(
  585. std::string& dir) const
  586. {
  587. return vsSetupAPIHelper.GetVSInstanceInfo(dir);
  588. }
  589. cm::optional<std::string>
  590. cmGlobalVisualStudioVersionedGenerator::GetVSInstanceVersion() const
  591. {
  592. cm::optional<std::string> result;
  593. std::string vsInstanceVersion;
  594. if (vsSetupAPIHelper.GetVSInstanceVersion(vsInstanceVersion)) {
  595. result = vsInstanceVersion;
  596. }
  597. return result;
  598. }
  599. bool cmGlobalVisualStudioVersionedGenerator::IsStdOutEncodingSupported() const
  600. {
  601. // Supported from Visual Studio 16.7 Preview 3.
  602. if (this->Version > cmGlobalVisualStudioGenerator::VSVersion::VS16) {
  603. return true;
  604. }
  605. if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS16) {
  606. return false;
  607. }
  608. static std::string const vsVer16_7_P2 = "16.7.30128.36";
  609. cm::optional<std::string> vsVer = this->GetVSInstanceVersion();
  610. return (vsVer &&
  611. cmSystemTools::VersionCompareGreaterEq(*vsVer, vsVer16_7_P2));
  612. }
  613. bool cmGlobalVisualStudioVersionedGenerator::IsUtf8EncodingSupported() const
  614. {
  615. // Supported from Visual Studio 16.10 Preview 2.
  616. if (this->Version > cmGlobalVisualStudioGenerator::VSVersion::VS16) {
  617. return true;
  618. }
  619. if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS16) {
  620. return false;
  621. }
  622. static std::string const vsVer16_10_P2 = "16.10.31213.239";
  623. cm::optional<std::string> vsVer = this->GetVSInstanceVersion();
  624. return (vsVer &&
  625. cmSystemTools::VersionCompareGreaterEq(*vsVer, vsVer16_10_P2));
  626. }
  627. const char*
  628. cmGlobalVisualStudioVersionedGenerator::GetAndroidApplicationTypeRevision()
  629. const
  630. {
  631. switch (this->Version) {
  632. case cmGlobalVisualStudioGenerator::VSVersion::VS9:
  633. case cmGlobalVisualStudioGenerator::VSVersion::VS10:
  634. case cmGlobalVisualStudioGenerator::VSVersion::VS11:
  635. case cmGlobalVisualStudioGenerator::VSVersion::VS12:
  636. return "";
  637. case cmGlobalVisualStudioGenerator::VSVersion::VS14:
  638. return "2.0";
  639. case cmGlobalVisualStudioGenerator::VSVersion::VS15:
  640. case cmGlobalVisualStudioGenerator::VSVersion::VS16:
  641. case cmGlobalVisualStudioGenerator::VSVersion::VS17:
  642. return "3.0";
  643. }
  644. return "";
  645. }
  646. cmGlobalVisualStudioVersionedGenerator::AuxToolset
  647. cmGlobalVisualStudioVersionedGenerator::FindAuxToolset(
  648. std::string& version, std::string& props) const
  649. {
  650. if (version.empty()) {
  651. return AuxToolset::None;
  652. }
  653. std::string instancePath;
  654. this->GetVSInstance(instancePath);
  655. cmSystemTools::ConvertToUnixSlashes(instancePath);
  656. // Translate three-component format accepted by "vcvarsall -vcvars_ver=".
  657. cmsys::RegularExpression threeComponent(
  658. "^([0-9]+\\.[0-9]+)\\.[0-9][0-9][0-9][0-9][0-9]$");
  659. if (threeComponent.find(version)) {
  660. // Load "VC/Auxiliary/Build/*/Microsoft.VCToolsVersion.*.txt" files
  661. // with two matching components to check their three-component version.
  662. std::string const& twoComponent = threeComponent.match(1);
  663. std::string pattern =
  664. cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, twoComponent,
  665. "*/Microsoft.VCToolsVersion."_s, twoComponent, "*.txt"_s);
  666. cmsys::Glob glob;
  667. glob.SetRecurseThroughSymlinks(false);
  668. if (glob.FindFiles(pattern)) {
  669. for (std::string const& txt : glob.GetFiles()) {
  670. std::string ver;
  671. cmsys::ifstream fin(txt.c_str());
  672. if (fin && std::getline(fin, ver)) {
  673. // Strip trailing whitespace.
  674. ver = ver.substr(0, ver.find_first_not_of("0123456789."));
  675. // If the three-component version matches, translate it to
  676. // that used by the "Microsoft.VCToolsVersion.*.txt" file name.
  677. if (ver == version) {
  678. cmsys::RegularExpression extractVersion(
  679. "VCToolsVersion\\.([0-9.]+)\\.txt$");
  680. if (extractVersion.find(txt)) {
  681. version = extractVersion.match(1);
  682. break;
  683. }
  684. }
  685. }
  686. }
  687. }
  688. }
  689. if (cmSystemTools::VersionCompareGreaterEq(version, "14.20")) {
  690. props = cmStrCat(instancePath, "/VC/Auxiliary/Build."_s, version,
  691. "/Microsoft.VCToolsVersion."_s, version, ".props"_s);
  692. if (cmSystemTools::PathExists(props)) {
  693. return AuxToolset::PropsExist;
  694. }
  695. }
  696. props = cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, version,
  697. "/Microsoft.VCToolsVersion."_s, version, ".props"_s);
  698. if (cmSystemTools::PathExists(props)) {
  699. return AuxToolset::PropsExist;
  700. }
  701. // Accept the toolset version that is default in the current VS version
  702. // by matching the name later VS versions will use for the SxS props files.
  703. std::string vcToolsetVersion;
  704. if (this->vsSetupAPIHelper.GetVCToolsetVersion(vcToolsetVersion)) {
  705. // Accept an exact-match (three-component version).
  706. if (version == vcToolsetVersion) {
  707. return AuxToolset::Default;
  708. }
  709. // Accept known SxS props file names using four version components
  710. // in VS versions later than the current.
  711. if (version == "14.28.16.9" && vcToolsetVersion == "14.28.29910") {
  712. return AuxToolset::Default;
  713. }
  714. if (version == "14.29.16.10" && vcToolsetVersion == "14.29.30037") {
  715. return AuxToolset::Default;
  716. }
  717. if (version == "14.29.16.11" && vcToolsetVersion == "14.29.30133") {
  718. return AuxToolset::Default;
  719. }
  720. // The first two components of the default toolset version typically
  721. // match the name used by later VS versions for the SxS props files.
  722. cmsys::RegularExpression twoComponent("^([0-9]+\\.[0-9]+)");
  723. if (twoComponent.find(version)) {
  724. std::string const versionPrefix = cmStrCat(twoComponent.match(1), '.');
  725. if (cmHasPrefix(vcToolsetVersion, versionPrefix)) {
  726. return AuxToolset::Default;
  727. }
  728. }
  729. }
  730. return AuxToolset::PropsMissing;
  731. }
  732. bool cmGlobalVisualStudioVersionedGenerator::InitializeWindows(cmMakefile* mf)
  733. {
  734. // If the Win 8.1 SDK is installed then we can select a SDK matching
  735. // the target Windows version.
  736. if (this->IsWin81SDKInstalled()) {
  737. // VS 2019 does not default to 8.1 so specify it explicitly when needed.
  738. if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16 &&
  739. !cmSystemTools::VersionCompareGreater(this->SystemVersion, "8.1")) {
  740. this->SetWindowsTargetPlatformVersion("8.1", mf);
  741. return true;
  742. }
  743. return cmGlobalVisualStudio14Generator::InitializeWindows(mf);
  744. }
  745. // Otherwise we must choose a Win 10 SDK even if we are not targeting
  746. // Windows 10.
  747. return this->SelectWindows10SDK(mf, false);
  748. }
  749. bool cmGlobalVisualStudioVersionedGenerator::SelectWindowsStoreToolset(
  750. std::string& toolset) const
  751. {
  752. if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) {
  753. if (this->IsWindowsStoreToolsetInstalled() &&
  754. this->IsWindowsDesktopToolsetInstalled()) {
  755. toolset = VSVersionToToolset(this->Version);
  756. return true;
  757. } else {
  758. return false;
  759. }
  760. }
  761. return this->cmGlobalVisualStudio14Generator::SelectWindowsStoreToolset(
  762. toolset);
  763. }
  764. bool cmGlobalVisualStudioVersionedGenerator::IsWindowsDesktopToolsetInstalled()
  765. const
  766. {
  767. return vsSetupAPIHelper.IsVSInstalled();
  768. }
  769. bool cmGlobalVisualStudioVersionedGenerator::IsWindowsStoreToolsetInstalled()
  770. const
  771. {
  772. return vsSetupAPIHelper.IsWin10SDKInstalled();
  773. }
  774. bool cmGlobalVisualStudioVersionedGenerator::IsWin81SDKInstalled() const
  775. {
  776. // Does the VS installer tool know about one?
  777. if (vsSetupAPIHelper.IsWin81SDKInstalled()) {
  778. return true;
  779. }
  780. // Does the registry know about one (e.g. from VS 2015)?
  781. std::string win81Root;
  782. if (cmSystemTools::ReadRegistryValue(
  783. "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\"
  784. "Windows Kits\\Installed Roots;KitsRoot81",
  785. win81Root, cmSystemTools::KeyWOW64_32) ||
  786. cmSystemTools::ReadRegistryValue(
  787. "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\"
  788. "Windows Kits\\Installed Roots;KitsRoot81",
  789. win81Root, cmSystemTools::KeyWOW64_32)) {
  790. return cmSystemTools::FileExists(win81Root + "/include/um/windows.h",
  791. true);
  792. }
  793. return false;
  794. }
  795. std::string
  796. cmGlobalVisualStudioVersionedGenerator::GetWindows10SDKMaxVersionDefault(
  797. cmMakefile*) const
  798. {
  799. return std::string();
  800. }
  801. cm::optional<std::string>
  802. cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommandEarly(cmMakefile* mf)
  803. {
  804. std::string instance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
  805. if (!this->SetGeneratorInstance(instance, mf)) {
  806. cmSystemTools::SetFatalErrorOccured();
  807. return {};
  808. }
  809. return this->cmGlobalVisualStudio14Generator::FindMSBuildCommandEarly(mf);
  810. }
  811. std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand()
  812. {
  813. std::string msbuild;
  814. // Ask Visual Studio Installer tool.
  815. std::string vs;
  816. if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
  817. if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS17) {
  818. msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe";
  819. if (cmSystemTools::FileExists(msbuild)) {
  820. return msbuild;
  821. }
  822. }
  823. msbuild = vs + "/MSBuild/Current/Bin/MSBuild.exe";
  824. if (cmSystemTools::FileExists(msbuild)) {
  825. return msbuild;
  826. }
  827. msbuild = vs + "/MSBuild/15.0/Bin/MSBuild.exe";
  828. if (cmSystemTools::FileExists(msbuild)) {
  829. return msbuild;
  830. }
  831. }
  832. msbuild = "MSBuild.exe";
  833. return msbuild;
  834. }
  835. std::string cmGlobalVisualStudioVersionedGenerator::FindDevEnvCommand()
  836. {
  837. std::string devenv;
  838. // Ask Visual Studio Installer tool.
  839. std::string vs;
  840. if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
  841. devenv = vs + "/Common7/IDE/devenv.com";
  842. if (cmSystemTools::FileExists(devenv)) {
  843. return devenv;
  844. }
  845. }
  846. devenv = "devenv.com";
  847. return devenv;
  848. }