cmGlobalVisualStudioVersionedGenerator.cxx 12 KB

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