cmVSSetupHelper.cxx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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 "cmVSSetupHelper.h"
  4. #include "cmSystemTools.h"
  5. #include "cmsys/Encoding.hxx"
  6. #include "cmsys/FStream.hxx"
  7. #ifndef VSSetupConstants
  8. #define VSSetupConstants
  9. /* clang-format off */
  10. const IID IID_ISetupConfiguration = {
  11. 0x42843719, 0xDB4C, 0x46C2,
  12. { 0x8E, 0x7C, 0x64, 0xF1, 0x81, 0x6E, 0xFD, 0x5B }
  13. };
  14. const IID IID_ISetupConfiguration2 = {
  15. 0x26AAB78C, 0x4A60, 0x49D6,
  16. { 0xAF, 0x3B, 0x3C, 0x35, 0xBC, 0x93, 0x36, 0x5D }
  17. };
  18. const IID IID_ISetupPackageReference = {
  19. 0xda8d8a16, 0xb2b6, 0x4487,
  20. { 0xa2, 0xf1, 0x59, 0x4c, 0xcc, 0xcd, 0x6b, 0xf5 }
  21. };
  22. const IID IID_ISetupHelper = {
  23. 0x42b21b78, 0x6192, 0x463e,
  24. { 0x87, 0xbf, 0xd5, 0x77, 0x83, 0x8f, 0x1d, 0x5c }
  25. };
  26. const IID IID_IEnumSetupInstances = {
  27. 0x6380BCFF, 0x41D3, 0x4B2E,
  28. { 0x8B, 0x2E, 0xBF, 0x8A, 0x68, 0x10, 0xC8, 0x48 }
  29. };
  30. const IID IID_ISetupInstance2 = {
  31. 0x89143C9A, 0x05AF, 0x49B0,
  32. { 0xB7, 0x17, 0x72, 0xE2, 0x18, 0xA2, 0x18, 0x5C }
  33. };
  34. const IID IID_ISetupInstance = {
  35. 0xB41463C3, 0x8866, 0x43B5,
  36. { 0xBC, 0x33, 0x2B, 0x06, 0x76, 0xF7, 0xF4, 0x2E }
  37. };
  38. const CLSID CLSID_SetupConfiguration = {
  39. 0x177F0C4A, 0x1CD3, 0x4DE7,
  40. { 0xA3, 0x2C, 0x71, 0xDB, 0xBB, 0x9F, 0xA3, 0x6D }
  41. };
  42. /* clang-format on */
  43. #endif
  44. const WCHAR* Win10SDKComponent =
  45. L"Microsoft.VisualStudio.Component.Windows10SDK";
  46. const WCHAR* Win81SDKComponent =
  47. L"Microsoft.VisualStudio.Component.Windows81SDK";
  48. const WCHAR* ComponentType = L"Component";
  49. std::string VSInstanceInfo::GetInstallLocation() const
  50. {
  51. std::string loc = cmsys::Encoding::ToNarrow(this->VSInstallLocation);
  52. cmSystemTools::ConvertToUnixSlashes(loc);
  53. return loc;
  54. }
  55. cmVSSetupAPIHelper::cmVSSetupAPIHelper()
  56. : setupConfig(NULL)
  57. , setupConfig2(NULL)
  58. , setupHelper(NULL)
  59. , initializationFailure(false)
  60. {
  61. comInitialized = CoInitializeEx(NULL, 0);
  62. if (SUCCEEDED(comInitialized)) {
  63. Initialize();
  64. } else {
  65. initializationFailure = true;
  66. }
  67. }
  68. cmVSSetupAPIHelper::~cmVSSetupAPIHelper()
  69. {
  70. setupHelper = NULL;
  71. setupConfig2 = NULL;
  72. setupConfig = NULL;
  73. if (SUCCEEDED(comInitialized))
  74. CoUninitialize();
  75. }
  76. bool cmVSSetupAPIHelper::SetVSInstance(std::string const& vsInstallLocation)
  77. {
  78. this->SpecifiedVSInstallLocation = vsInstallLocation;
  79. cmSystemTools::ConvertToUnixSlashes(this->SpecifiedVSInstallLocation);
  80. chosenInstanceInfo = VSInstanceInfo();
  81. return this->EnumerateAndChooseVSInstance();
  82. }
  83. bool cmVSSetupAPIHelper::IsVS2017Installed()
  84. {
  85. return this->EnumerateAndChooseVSInstance();
  86. }
  87. bool cmVSSetupAPIHelper::IsWin10SDKInstalled()
  88. {
  89. return (this->EnumerateAndChooseVSInstance() &&
  90. chosenInstanceInfo.IsWin10SDKInstalled);
  91. }
  92. bool cmVSSetupAPIHelper::IsWin81SDKInstalled()
  93. {
  94. return (this->EnumerateAndChooseVSInstance() &&
  95. chosenInstanceInfo.IsWin81SDKInstalled);
  96. }
  97. bool cmVSSetupAPIHelper::CheckInstalledComponent(
  98. SmartCOMPtr<ISetupPackageReference> package, bool& bWin10SDK,
  99. bool& bWin81SDK)
  100. {
  101. bool ret = false;
  102. bWin10SDK = bWin81SDK = false;
  103. SmartBSTR bstrId;
  104. if (FAILED(package->GetId(&bstrId))) {
  105. return ret;
  106. }
  107. SmartBSTR bstrType;
  108. if (FAILED(package->GetType(&bstrType))) {
  109. return ret;
  110. }
  111. std::wstring id = std::wstring(bstrId);
  112. std::wstring type = std::wstring(bstrType);
  113. // Checks for any version of Win10 SDK. The version is appended at the end of
  114. // the
  115. // component name ex: Microsoft.VisualStudio.Component.Windows10SDK.10240
  116. if (id.find(Win10SDKComponent) != std::wstring::npos &&
  117. type.compare(ComponentType) == 0) {
  118. bWin10SDK = true;
  119. ret = true;
  120. }
  121. if (id.compare(Win81SDKComponent) == 0 && type.compare(ComponentType) == 0) {
  122. bWin81SDK = true;
  123. ret = true;
  124. }
  125. return ret;
  126. }
  127. // Gather additional info such as if VCToolset, WinSDKs are installed, location
  128. // of VS and version information.
  129. bool cmVSSetupAPIHelper::GetVSInstanceInfo(
  130. SmartCOMPtr<ISetupInstance2> pInstance, VSInstanceInfo& vsInstanceInfo)
  131. {
  132. if (pInstance == NULL)
  133. return false;
  134. SmartBSTR bstrId;
  135. if (SUCCEEDED(pInstance->GetInstanceId(&bstrId))) {
  136. vsInstanceInfo.InstanceId = std::wstring(bstrId);
  137. } else {
  138. return false;
  139. }
  140. InstanceState state;
  141. if (FAILED(pInstance->GetState(&state))) {
  142. return false;
  143. }
  144. ULONGLONG ullVersion = 0;
  145. SmartBSTR bstrVersion;
  146. if (FAILED(pInstance->GetInstallationVersion(&bstrVersion))) {
  147. return false;
  148. } else {
  149. vsInstanceInfo.Version = std::wstring(bstrVersion);
  150. if (FAILED(setupHelper->ParseVersion(bstrVersion, &ullVersion))) {
  151. vsInstanceInfo.ullVersion = 0;
  152. } else {
  153. vsInstanceInfo.ullVersion = ullVersion;
  154. }
  155. }
  156. // Reboot may have been required before the installation path was created.
  157. SmartBSTR bstrInstallationPath;
  158. if ((eLocal & state) == eLocal) {
  159. if (FAILED(pInstance->GetInstallationPath(&bstrInstallationPath))) {
  160. return false;
  161. } else {
  162. vsInstanceInfo.VSInstallLocation = std::wstring(bstrInstallationPath);
  163. }
  164. }
  165. // Check if a compiler is installed with this instance.
  166. {
  167. std::string const vcRoot = vsInstanceInfo.GetInstallLocation();
  168. std::string const vcToolsVersionFile =
  169. vcRoot + "/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt";
  170. std::string vcToolsVersion;
  171. cmsys::ifstream fin(vcToolsVersionFile.c_str());
  172. if (!fin || !cmSystemTools::GetLineFromStream(fin, vcToolsVersion)) {
  173. return false;
  174. }
  175. vcToolsVersion = cmSystemTools::TrimWhitespace(vcToolsVersion);
  176. std::string const vcToolsDir = vcRoot + "/VC/Tools/MSVC/" + vcToolsVersion;
  177. if (!cmSystemTools::FileIsDirectory(vcToolsDir)) {
  178. return false;
  179. }
  180. }
  181. // Reboot may have been required before the product package was registered
  182. // (last).
  183. if ((eRegistered & state) == eRegistered) {
  184. SmartCOMPtr<ISetupPackageReference> product;
  185. if (FAILED(pInstance->GetProduct(&product)) || !product) {
  186. return false;
  187. }
  188. LPSAFEARRAY lpsaPackages;
  189. if (FAILED(pInstance->GetPackages(&lpsaPackages)) ||
  190. lpsaPackages == NULL) {
  191. return false;
  192. }
  193. int lower = lpsaPackages->rgsabound[0].lLbound;
  194. int upper = lpsaPackages->rgsabound[0].cElements + lower;
  195. IUnknown** ppData = (IUnknown**)lpsaPackages->pvData;
  196. for (int i = lower; i < upper; i++) {
  197. SmartCOMPtr<ISetupPackageReference> package = NULL;
  198. if (FAILED(ppData[i]->QueryInterface(IID_ISetupPackageReference,
  199. (void**)&package)) ||
  200. package == NULL)
  201. continue;
  202. bool win10SDKInstalled = false;
  203. bool win81SDkInstalled = false;
  204. bool ret =
  205. CheckInstalledComponent(package, win10SDKInstalled, win81SDkInstalled);
  206. if (ret) {
  207. vsInstanceInfo.IsWin10SDKInstalled |= win10SDKInstalled;
  208. vsInstanceInfo.IsWin81SDKInstalled |= win81SDkInstalled;
  209. }
  210. }
  211. SafeArrayDestroy(lpsaPackages);
  212. }
  213. return true;
  214. }
  215. bool cmVSSetupAPIHelper::GetVSInstanceInfo(std::string& vsInstallLocation)
  216. {
  217. vsInstallLocation.clear();
  218. bool isInstalled = this->EnumerateAndChooseVSInstance();
  219. if (isInstalled) {
  220. vsInstallLocation = chosenInstanceInfo.GetInstallLocation();
  221. }
  222. return isInstalled;
  223. }
  224. bool cmVSSetupAPIHelper::EnumerateAndChooseVSInstance()
  225. {
  226. bool isVSInstanceExists = false;
  227. if (chosenInstanceInfo.VSInstallLocation.compare(L"") != 0) {
  228. return true;
  229. }
  230. if (initializationFailure || setupConfig == NULL || setupConfig2 == NULL ||
  231. setupHelper == NULL)
  232. return false;
  233. std::string envVSCommonToolsDir;
  234. // FIXME: When we support VS versions beyond 2017, the version
  235. // to choose will be passed in by the caller. We need to map that
  236. // to a per-version name of this environment variable.
  237. if (cmSystemTools::GetEnv("VS150COMNTOOLS", envVSCommonToolsDir)) {
  238. cmSystemTools::ConvertToUnixSlashes(envVSCommonToolsDir);
  239. }
  240. std::vector<VSInstanceInfo> vecVSInstances;
  241. SmartCOMPtr<IEnumSetupInstances> enumInstances = NULL;
  242. if (FAILED(
  243. setupConfig2->EnumInstances((IEnumSetupInstances**)&enumInstances)) ||
  244. !enumInstances) {
  245. return false;
  246. }
  247. SmartCOMPtr<ISetupInstance> instance;
  248. while (SUCCEEDED(enumInstances->Next(1, &instance, NULL)) && instance) {
  249. SmartCOMPtr<ISetupInstance2> instance2 = NULL;
  250. if (FAILED(
  251. instance->QueryInterface(IID_ISetupInstance2, (void**)&instance2)) ||
  252. !instance2) {
  253. instance = NULL;
  254. continue;
  255. }
  256. VSInstanceInfo instanceInfo;
  257. bool isInstalled = GetVSInstanceInfo(instance2, instanceInfo);
  258. instance = instance2 = NULL;
  259. if (isInstalled) {
  260. if (!this->SpecifiedVSInstallLocation.empty()) {
  261. // We are looking for a specific instance.
  262. std::string currentVSLocation = instanceInfo.GetInstallLocation();
  263. if (cmSystemTools::ComparePath(currentVSLocation,
  264. this->SpecifiedVSInstallLocation)) {
  265. chosenInstanceInfo = instanceInfo;
  266. return true;
  267. }
  268. } else {
  269. // We are not looking for a specific instance.
  270. // If we've been given a hint then use it.
  271. if (!envVSCommonToolsDir.empty()) {
  272. std::string currentVSLocation = instanceInfo.GetInstallLocation();
  273. currentVSLocation += "/Common7/Tools";
  274. if (cmSystemTools::ComparePath(currentVSLocation,
  275. envVSCommonToolsDir)) {
  276. chosenInstanceInfo = instanceInfo;
  277. return true;
  278. }
  279. }
  280. // Otherwise, add this to the list of candidates.
  281. vecVSInstances.push_back(instanceInfo);
  282. }
  283. }
  284. }
  285. if (vecVSInstances.size() > 0) {
  286. isVSInstanceExists = true;
  287. int index = ChooseVSInstance(vecVSInstances);
  288. chosenInstanceInfo = vecVSInstances[index];
  289. }
  290. return isVSInstanceExists;
  291. }
  292. int cmVSSetupAPIHelper::ChooseVSInstance(
  293. const std::vector<VSInstanceInfo>& vecVSInstances)
  294. {
  295. if (vecVSInstances.size() == 0)
  296. return -1;
  297. if (vecVSInstances.size() == 1)
  298. return 0;
  299. unsigned int chosenIndex = 0;
  300. for (unsigned int i = 1; i < vecVSInstances.size(); i++) {
  301. // If the current has Win10 SDK but not the chosen one, then choose the
  302. // current VS instance
  303. if (!vecVSInstances[chosenIndex].IsWin10SDKInstalled &&
  304. vecVSInstances[i].IsWin10SDKInstalled) {
  305. chosenIndex = i;
  306. continue;
  307. }
  308. // If the chosen one has Win10 SDK but the current one is not, then look at
  309. // the next VS instance even the current
  310. // instance version may be higher
  311. if (vecVSInstances[chosenIndex].IsWin10SDKInstalled &&
  312. !vecVSInstances[i].IsWin10SDKInstalled) {
  313. continue;
  314. }
  315. // If both chosen one and current one doesn't have Win10 SDK but the
  316. // current one has Win8.1 SDK installed,
  317. // then choose the current one
  318. if (!vecVSInstances[chosenIndex].IsWin10SDKInstalled &&
  319. !vecVSInstances[i].IsWin10SDKInstalled &&
  320. !vecVSInstances[chosenIndex].IsWin81SDKInstalled &&
  321. vecVSInstances[i].IsWin81SDKInstalled) {
  322. chosenIndex = i;
  323. continue;
  324. }
  325. // If there is no difference in WinSDKs then look for the highest version
  326. // of installed VS
  327. if ((vecVSInstances[chosenIndex].IsWin10SDKInstalled ==
  328. vecVSInstances[i].IsWin10SDKInstalled) &&
  329. (vecVSInstances[chosenIndex].IsWin81SDKInstalled ==
  330. vecVSInstances[i].IsWin81SDKInstalled) &&
  331. vecVSInstances[chosenIndex].Version < vecVSInstances[i].Version) {
  332. chosenIndex = i;
  333. continue;
  334. }
  335. }
  336. return chosenIndex;
  337. }
  338. bool cmVSSetupAPIHelper::Initialize()
  339. {
  340. if (initializationFailure)
  341. return false;
  342. if (FAILED(comInitialized)) {
  343. initializationFailure = true;
  344. return false;
  345. }
  346. if (FAILED(setupConfig.CoCreateInstance(CLSID_SetupConfiguration, NULL,
  347. IID_ISetupConfiguration,
  348. CLSCTX_INPROC_SERVER)) ||
  349. setupConfig == NULL) {
  350. initializationFailure = true;
  351. return false;
  352. }
  353. if (FAILED(setupConfig.QueryInterface(IID_ISetupConfiguration2,
  354. (void**)&setupConfig2)) ||
  355. setupConfig2 == NULL) {
  356. initializationFailure = true;
  357. return false;
  358. }
  359. if (FAILED(
  360. setupConfig.QueryInterface(IID_ISetupHelper, (void**)&setupHelper)) ||
  361. setupHelper == NULL) {
  362. initializationFailure = true;
  363. return false;
  364. }
  365. initializationFailure = false;
  366. return true;
  367. }