cmGlobalVisualStudioVersionedGenerator.cxx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  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 "cmAlgorithms.h"
  5. #include "cmDocumentationEntry.h"
  6. #include "cmLocalVisualStudio10Generator.h"
  7. #include "cmMakefile.h"
  8. #include "cmVSSetupHelper.h"
  9. #include "cmake.h"
  10. #if defined(_M_ARM64)
  11. # define HOST_PLATFORM_NAME "ARM64"
  12. #elif defined(_M_ARM)
  13. # define HOST_PLATFORM_NAME "ARM"
  14. #elif defined(_M_IA64)
  15. # define HOST_PLATFORM_NAME "Itanium"
  16. #else
  17. # include "cmsys/SystemInformation.hxx"
  18. #endif
  19. static std::string VSHostPlatformName()
  20. {
  21. #ifdef HOST_PLATFORM_NAME
  22. return HOST_PLATFORM_NAME;
  23. #else
  24. cmsys::SystemInformation info;
  25. if (info.Is64Bits()) {
  26. return "x64";
  27. } else {
  28. return "Win32";
  29. }
  30. #endif
  31. }
  32. static unsigned int VSVersionToMajor(
  33. cmGlobalVisualStudioGenerator::VSVersion v)
  34. {
  35. switch (v) {
  36. case cmGlobalVisualStudioGenerator::VS9:
  37. return 9;
  38. case cmGlobalVisualStudioGenerator::VS10:
  39. return 10;
  40. case cmGlobalVisualStudioGenerator::VS11:
  41. return 11;
  42. case cmGlobalVisualStudioGenerator::VS12:
  43. return 12;
  44. case cmGlobalVisualStudioGenerator::VS14:
  45. return 14;
  46. case cmGlobalVisualStudioGenerator::VS15:
  47. return 15;
  48. case cmGlobalVisualStudioGenerator::VS16:
  49. return 16;
  50. }
  51. return 0;
  52. }
  53. static const char* VSVersionToToolset(
  54. cmGlobalVisualStudioGenerator::VSVersion v)
  55. {
  56. switch (v) {
  57. case cmGlobalVisualStudioGenerator::VS9:
  58. return "v90";
  59. case cmGlobalVisualStudioGenerator::VS10:
  60. return "v100";
  61. case cmGlobalVisualStudioGenerator::VS11:
  62. return "v110";
  63. case cmGlobalVisualStudioGenerator::VS12:
  64. return "v120";
  65. case cmGlobalVisualStudioGenerator::VS14:
  66. return "v140";
  67. case cmGlobalVisualStudioGenerator::VS15:
  68. return "v141";
  69. case cmGlobalVisualStudioGenerator::VS16:
  70. return "v142";
  71. }
  72. return "";
  73. }
  74. static const char vs15generatorName[] = "Visual Studio 15 2017";
  75. // Map generator name without year to name with year.
  76. static const char* cmVS15GenName(const std::string& name, std::string& genName)
  77. {
  78. if (strncmp(name.c_str(), vs15generatorName,
  79. sizeof(vs15generatorName) - 6) != 0) {
  80. return 0;
  81. }
  82. const char* p = name.c_str() + sizeof(vs15generatorName) - 6;
  83. if (cmHasLiteralPrefix(p, " 2017")) {
  84. p += 5;
  85. }
  86. genName = std::string(vs15generatorName) + p;
  87. return p;
  88. }
  89. class cmGlobalVisualStudioVersionedGenerator::Factory15
  90. : public cmGlobalGeneratorFactory
  91. {
  92. public:
  93. cmGlobalGenerator* CreateGlobalGenerator(const std::string& name,
  94. cmake* cm) const override
  95. {
  96. std::string genName;
  97. const char* p = cmVS15GenName(name, genName);
  98. if (!p) {
  99. return 0;
  100. }
  101. if (!*p) {
  102. return new cmGlobalVisualStudioVersionedGenerator(
  103. cmGlobalVisualStudioGenerator::VS15, cm, genName, "");
  104. }
  105. if (*p++ != ' ') {
  106. return 0;
  107. }
  108. if (strcmp(p, "Win64") == 0) {
  109. return new cmGlobalVisualStudioVersionedGenerator(
  110. cmGlobalVisualStudioGenerator::VS15, cm, genName, "x64");
  111. }
  112. if (strcmp(p, "ARM") == 0) {
  113. return new cmGlobalVisualStudioVersionedGenerator(
  114. cmGlobalVisualStudioGenerator::VS15, cm, genName, "ARM");
  115. }
  116. return 0;
  117. }
  118. void GetDocumentation(cmDocumentationEntry& entry) const override
  119. {
  120. entry.Name = std::string(vs15generatorName) + " [arch]";
  121. entry.Brief = "Generates Visual Studio 2017 project files. "
  122. "Optional [arch] can be \"Win64\" or \"ARM\".";
  123. }
  124. std::vector<std::string> GetGeneratorNames() const override
  125. {
  126. std::vector<std::string> names;
  127. names.push_back(vs15generatorName);
  128. return names;
  129. }
  130. std::vector<std::string> GetGeneratorNamesWithPlatform() const override
  131. {
  132. std::vector<std::string> names;
  133. names.push_back(vs15generatorName + std::string(" ARM"));
  134. names.push_back(vs15generatorName + std::string(" Win64"));
  135. return names;
  136. }
  137. bool SupportsToolset() const override { return true; }
  138. bool SupportsPlatform() const override { return true; }
  139. std::vector<std::string> GetKnownPlatforms() const override
  140. {
  141. std::vector<std::string> platforms;
  142. platforms.emplace_back("x64");
  143. platforms.emplace_back("Win32");
  144. platforms.emplace_back("ARM");
  145. platforms.emplace_back("ARM64");
  146. return platforms;
  147. }
  148. std::string GetDefaultPlatformName() const override { return "Win32"; }
  149. };
  150. cmGlobalGeneratorFactory*
  151. cmGlobalVisualStudioVersionedGenerator::NewFactory15()
  152. {
  153. return new Factory15;
  154. }
  155. static const char vs16generatorName[] = "Visual Studio 16 2019";
  156. // Map generator name without year to name with year.
  157. static const char* cmVS16GenName(const std::string& name, std::string& genName)
  158. {
  159. if (strncmp(name.c_str(), vs16generatorName,
  160. sizeof(vs16generatorName) - 6) != 0) {
  161. return 0;
  162. }
  163. const char* p = name.c_str() + sizeof(vs16generatorName) - 6;
  164. if (cmHasLiteralPrefix(p, " 2019")) {
  165. p += 5;
  166. }
  167. genName = std::string(vs16generatorName) + p;
  168. return p;
  169. }
  170. class cmGlobalVisualStudioVersionedGenerator::Factory16
  171. : public cmGlobalGeneratorFactory
  172. {
  173. public:
  174. virtual cmGlobalGenerator* CreateGlobalGenerator(const std::string& name,
  175. cmake* cm) const
  176. {
  177. std::string genName;
  178. const char* p = cmVS16GenName(name, genName);
  179. if (!p) {
  180. return 0;
  181. }
  182. if (!*p) {
  183. return new cmGlobalVisualStudioVersionedGenerator(
  184. cmGlobalVisualStudioGenerator::VS16, cm, genName, "");
  185. }
  186. return 0;
  187. }
  188. virtual void GetDocumentation(cmDocumentationEntry& entry) const
  189. {
  190. entry.Name = std::string(vs16generatorName);
  191. entry.Brief = "Generates Visual Studio 2019 project files. "
  192. "Use -A option to specify architecture.";
  193. }
  194. std::vector<std::string> GetGeneratorNames() const override
  195. {
  196. std::vector<std::string> names;
  197. names.push_back(vs16generatorName);
  198. return names;
  199. }
  200. std::vector<std::string> GetGeneratorNamesWithPlatform() const override
  201. {
  202. return std::vector<std::string>();
  203. }
  204. bool SupportsToolset() const override { return true; }
  205. bool SupportsPlatform() const override { return true; }
  206. std::vector<std::string> GetKnownPlatforms() const override
  207. {
  208. std::vector<std::string> platforms;
  209. platforms.emplace_back("x64");
  210. platforms.emplace_back("Win32");
  211. platforms.emplace_back("ARM");
  212. platforms.emplace_back("ARM64");
  213. return platforms;
  214. }
  215. std::string GetDefaultPlatformName() const override
  216. {
  217. return VSHostPlatformName();
  218. }
  219. };
  220. cmGlobalGeneratorFactory*
  221. cmGlobalVisualStudioVersionedGenerator::NewFactory16()
  222. {
  223. return new Factory16;
  224. }
  225. cmGlobalVisualStudioVersionedGenerator::cmGlobalVisualStudioVersionedGenerator(
  226. VSVersion version, cmake* cm, const std::string& name,
  227. std::string const& platformInGeneratorName)
  228. : cmGlobalVisualStudio14Generator(cm, name, platformInGeneratorName)
  229. , vsSetupAPIHelper(VSVersionToMajor(version))
  230. {
  231. this->Version = version;
  232. this->ExpressEdition = false;
  233. this->DefaultPlatformToolset = VSVersionToToolset(this->Version);
  234. this->DefaultCLFlagTableName = VSVersionToToolset(this->Version);
  235. this->DefaultCSharpFlagTableName = VSVersionToToolset(this->Version);
  236. this->DefaultLinkFlagTableName = VSVersionToToolset(this->Version);
  237. if (this->Version >= cmGlobalVisualStudioGenerator::VS16) {
  238. this->DefaultPlatformName = VSHostPlatformName();
  239. }
  240. }
  241. bool cmGlobalVisualStudioVersionedGenerator::MatchesGeneratorName(
  242. const std::string& name) const
  243. {
  244. std::string genName;
  245. switch (this->Version) {
  246. case cmGlobalVisualStudioGenerator::VS9:
  247. case cmGlobalVisualStudioGenerator::VS10:
  248. case cmGlobalVisualStudioGenerator::VS11:
  249. case cmGlobalVisualStudioGenerator::VS12:
  250. case cmGlobalVisualStudioGenerator::VS14:
  251. break;
  252. case cmGlobalVisualStudioGenerator::VS15:
  253. if (cmVS15GenName(name, genName)) {
  254. return genName == this->GetName();
  255. }
  256. break;
  257. case cmGlobalVisualStudioGenerator::VS16:
  258. if (cmVS16GenName(name, genName)) {
  259. return genName == this->GetName();
  260. }
  261. break;
  262. }
  263. return false;
  264. }
  265. bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
  266. std::string const& i, cmMakefile* mf)
  267. {
  268. if (!i.empty()) {
  269. if (!this->vsSetupAPIHelper.SetVSInstance(i)) {
  270. std::ostringstream e;
  271. /* clang-format off */
  272. e <<
  273. "Generator\n"
  274. " " << this->GetName() << "\n"
  275. "could not find specified instance of Visual Studio:\n"
  276. " " << i;
  277. /* clang-format on */
  278. mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
  279. return false;
  280. }
  281. }
  282. std::string vsInstance;
  283. if (!this->vsSetupAPIHelper.GetVSInstanceInfo(vsInstance)) {
  284. std::ostringstream e;
  285. /* clang-format off */
  286. e <<
  287. "Generator\n"
  288. " " << this->GetName() << "\n"
  289. "could not find any instance of Visual Studio.\n";
  290. /* clang-format on */
  291. mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
  292. return false;
  293. }
  294. // Save the selected instance persistently.
  295. std::string genInstance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
  296. if (vsInstance != genInstance) {
  297. this->CMakeInstance->AddCacheEntry(
  298. "CMAKE_GENERATOR_INSTANCE", vsInstance.c_str(),
  299. "Generator instance identifier.", cmStateEnums::INTERNAL);
  300. }
  301. return true;
  302. }
  303. bool cmGlobalVisualStudioVersionedGenerator::GetVSInstance(
  304. std::string& dir) const
  305. {
  306. return vsSetupAPIHelper.GetVSInstanceInfo(dir);
  307. }
  308. bool cmGlobalVisualStudioVersionedGenerator::IsDefaultToolset(
  309. const std::string& version) const
  310. {
  311. if (version.empty()) {
  312. return true;
  313. }
  314. std::string vcToolsetVersion;
  315. if (this->vsSetupAPIHelper.GetVCToolsetVersion(vcToolsetVersion)) {
  316. cmsys::RegularExpression regex("[0-9][0-9]\\.[0-9]+");
  317. if (regex.find(version) && regex.find(vcToolsetVersion)) {
  318. const auto majorMinorEnd = vcToolsetVersion.find('.', 3);
  319. const auto majorMinor = vcToolsetVersion.substr(0, majorMinorEnd);
  320. return version == majorMinor;
  321. }
  322. }
  323. return false;
  324. }
  325. std::string cmGlobalVisualStudioVersionedGenerator::GetAuxiliaryToolset() const
  326. {
  327. const char* version = this->GetPlatformToolsetVersion();
  328. if (version) {
  329. std::string instancePath;
  330. GetVSInstance(instancePath);
  331. std::stringstream path;
  332. path << instancePath;
  333. path << "/VC/Auxiliary/Build/";
  334. path << version;
  335. path << "/Microsoft.VCToolsVersion." << version << ".props";
  336. std::string toolsetPath = path.str();
  337. cmSystemTools::ConvertToUnixSlashes(toolsetPath);
  338. return toolsetPath;
  339. }
  340. return {};
  341. }
  342. bool cmGlobalVisualStudioVersionedGenerator::InitializeWindows(cmMakefile* mf)
  343. {
  344. // If the Win 8.1 SDK is installed then we can select a SDK matching
  345. // the target Windows version.
  346. if (this->IsWin81SDKInstalled()) {
  347. return cmGlobalVisualStudio14Generator::InitializeWindows(mf);
  348. }
  349. // Otherwise we must choose a Win 10 SDK even if we are not targeting
  350. // Windows 10.
  351. return this->SelectWindows10SDK(mf, false);
  352. }
  353. bool cmGlobalVisualStudioVersionedGenerator::SelectWindowsStoreToolset(
  354. std::string& toolset) const
  355. {
  356. if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) {
  357. if (this->IsWindowsStoreToolsetInstalled() &&
  358. this->IsWindowsDesktopToolsetInstalled()) {
  359. toolset = VSVersionToToolset(this->Version);
  360. return true;
  361. } else {
  362. return false;
  363. }
  364. }
  365. return this->cmGlobalVisualStudio14Generator::SelectWindowsStoreToolset(
  366. toolset);
  367. }
  368. bool cmGlobalVisualStudioVersionedGenerator::IsWindowsDesktopToolsetInstalled()
  369. const
  370. {
  371. return vsSetupAPIHelper.IsVSInstalled();
  372. }
  373. bool cmGlobalVisualStudioVersionedGenerator::IsWindowsStoreToolsetInstalled()
  374. const
  375. {
  376. return vsSetupAPIHelper.IsWin10SDKInstalled();
  377. }
  378. bool cmGlobalVisualStudioVersionedGenerator::IsWin81SDKInstalled() const
  379. {
  380. // Does the VS installer tool know about one?
  381. if (vsSetupAPIHelper.IsWin81SDKInstalled()) {
  382. return true;
  383. }
  384. // Does the registry know about one (e.g. from VS 2015)?
  385. std::string win81Root;
  386. if (cmSystemTools::ReadRegistryValue(
  387. "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\"
  388. "Windows Kits\\Installed Roots;KitsRoot81",
  389. win81Root, cmSystemTools::KeyWOW64_32) ||
  390. cmSystemTools::ReadRegistryValue(
  391. "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\"
  392. "Windows Kits\\Installed Roots;KitsRoot81",
  393. win81Root, cmSystemTools::KeyWOW64_32)) {
  394. return cmSystemTools::FileExists(win81Root + "/um/windows.h", true);
  395. }
  396. return false;
  397. }
  398. std::string cmGlobalVisualStudioVersionedGenerator::GetWindows10SDKMaxVersion()
  399. const
  400. {
  401. return std::string();
  402. }
  403. std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand()
  404. {
  405. std::string msbuild;
  406. // Ask Visual Studio Installer tool.
  407. std::string vs;
  408. if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
  409. msbuild = vs + "/MSBuild/Current/Bin/MSBuild.exe";
  410. if (cmSystemTools::FileExists(msbuild)) {
  411. return msbuild;
  412. }
  413. msbuild = vs + "/MSBuild/15.0/Bin/MSBuild.exe";
  414. if (cmSystemTools::FileExists(msbuild)) {
  415. return msbuild;
  416. }
  417. }
  418. msbuild = "MSBuild.exe";
  419. return msbuild;
  420. }
  421. std::string cmGlobalVisualStudioVersionedGenerator::FindDevEnvCommand()
  422. {
  423. std::string devenv;
  424. // Ask Visual Studio Installer tool.
  425. std::string vs;
  426. if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
  427. devenv = vs + "/Common7/IDE/devenv.com";
  428. if (cmSystemTools::FileExists(devenv)) {
  429. return devenv;
  430. }
  431. }
  432. devenv = "devenv.com";
  433. return devenv;
  434. }