cmGlobalVisualStudioVersionedGenerator.cxx 14 KB

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