msvc.py 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826
  1. """
  2. Improved support for Microsoft Visual C++ compilers.
  3. Known supported compilers:
  4. --------------------------
  5. Microsoft Visual C++ 9.0:
  6. Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64)
  7. Microsoft Windows SDK 6.1 (x86, x64, ia64)
  8. Microsoft Windows SDK 7.0 (x86, x64, ia64)
  9. Microsoft Visual C++ 10.0:
  10. Microsoft Windows SDK 7.1 (x86, x64, ia64)
  11. Microsoft Visual C++ 14.X:
  12. Microsoft Visual C++ Build Tools 2015 (x86, x64, arm)
  13. Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64)
  14. Microsoft Visual Studio Build Tools 2019 (x86, x64, arm, arm64)
  15. This may also support compilers shipped with compatible Visual Studio versions.
  16. """
  17. import json
  18. from io import open
  19. from os import listdir, pathsep
  20. from os.path import join, isfile, isdir, dirname
  21. import sys
  22. import contextlib
  23. import platform
  24. import itertools
  25. import subprocess
  26. import distutils.errors
  27. from setuptools.extern.packaging.version import LegacyVersion
  28. from .monkey import get_unpatched
  29. if platform.system() == 'Windows':
  30. import winreg
  31. from os import environ
  32. else:
  33. # Mock winreg and environ so the module can be imported on this platform.
  34. class winreg:
  35. HKEY_USERS = None
  36. HKEY_CURRENT_USER = None
  37. HKEY_LOCAL_MACHINE = None
  38. HKEY_CLASSES_ROOT = None
  39. environ = dict()
  40. _msvc9_suppress_errors = (
  41. # msvc9compiler isn't available on some platforms
  42. ImportError,
  43. # msvc9compiler raises DistutilsPlatformError in some
  44. # environments. See #1118.
  45. distutils.errors.DistutilsPlatformError,
  46. )
  47. try:
  48. from distutils.msvc9compiler import Reg
  49. except _msvc9_suppress_errors:
  50. pass
  51. def msvc9_find_vcvarsall(version):
  52. """
  53. Patched "distutils.msvc9compiler.find_vcvarsall" to use the standalone
  54. compiler build for Python
  55. (VCForPython / Microsoft Visual C++ Compiler for Python 2.7).
  56. Fall back to original behavior when the standalone compiler is not
  57. available.
  58. Redirect the path of "vcvarsall.bat".
  59. Parameters
  60. ----------
  61. version: float
  62. Required Microsoft Visual C++ version.
  63. Return
  64. ------
  65. str
  66. vcvarsall.bat path
  67. """
  68. vc_base = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f'
  69. key = vc_base % ('', version)
  70. try:
  71. # Per-user installs register the compiler path here
  72. productdir = Reg.get_value(key, "installdir")
  73. except KeyError:
  74. try:
  75. # All-user installs on a 64-bit system register here
  76. key = vc_base % ('Wow6432Node\\', version)
  77. productdir = Reg.get_value(key, "installdir")
  78. except KeyError:
  79. productdir = None
  80. if productdir:
  81. vcvarsall = join(productdir, "vcvarsall.bat")
  82. if isfile(vcvarsall):
  83. return vcvarsall
  84. return get_unpatched(msvc9_find_vcvarsall)(version)
  85. def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs):
  86. """
  87. Patched "distutils.msvc9compiler.query_vcvarsall" for support extra
  88. Microsoft Visual C++ 9.0 and 10.0 compilers.
  89. Set environment without use of "vcvarsall.bat".
  90. Parameters
  91. ----------
  92. ver: float
  93. Required Microsoft Visual C++ version.
  94. arch: str
  95. Target architecture.
  96. Return
  97. ------
  98. dict
  99. environment
  100. """
  101. # Try to get environment from vcvarsall.bat (Classical way)
  102. try:
  103. orig = get_unpatched(msvc9_query_vcvarsall)
  104. return orig(ver, arch, *args, **kwargs)
  105. except distutils.errors.DistutilsPlatformError:
  106. # Pass error if Vcvarsall.bat is missing
  107. pass
  108. except ValueError:
  109. # Pass error if environment not set after executing vcvarsall.bat
  110. pass
  111. # If error, try to set environment directly
  112. try:
  113. return EnvironmentInfo(arch, ver).return_env()
  114. except distutils.errors.DistutilsPlatformError as exc:
  115. _augment_exception(exc, ver, arch)
  116. raise
  117. def _msvc14_find_vc2015():
  118. """Python 3.8 "distutils/_msvccompiler.py" backport"""
  119. try:
  120. key = winreg.OpenKey(
  121. winreg.HKEY_LOCAL_MACHINE,
  122. r"Software\Microsoft\VisualStudio\SxS\VC7",
  123. 0,
  124. winreg.KEY_READ | winreg.KEY_WOW64_32KEY
  125. )
  126. except OSError:
  127. return None, None
  128. best_version = 0
  129. best_dir = None
  130. with key:
  131. for i in itertools.count():
  132. try:
  133. v, vc_dir, vt = winreg.EnumValue(key, i)
  134. except OSError:
  135. break
  136. if v and vt == winreg.REG_SZ and isdir(vc_dir):
  137. try:
  138. version = int(float(v))
  139. except (ValueError, TypeError):
  140. continue
  141. if version >= 14 and version > best_version:
  142. best_version, best_dir = version, vc_dir
  143. return best_version, best_dir
  144. def _msvc14_find_vc2017():
  145. """Python 3.8 "distutils/_msvccompiler.py" backport
  146. Returns "15, path" based on the result of invoking vswhere.exe
  147. If no install is found, returns "None, None"
  148. The version is returned to avoid unnecessarily changing the function
  149. result. It may be ignored when the path is not None.
  150. If vswhere.exe is not available, by definition, VS 2017 is not
  151. installed.
  152. """
  153. root = environ.get("ProgramFiles(x86)") or environ.get("ProgramFiles")
  154. if not root:
  155. return None, None
  156. try:
  157. path = subprocess.check_output([
  158. join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"),
  159. "-latest",
  160. "-prerelease",
  161. "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
  162. "-property", "installationPath",
  163. "-products", "*",
  164. ]).decode(encoding="mbcs", errors="strict").strip()
  165. except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
  166. return None, None
  167. path = join(path, "VC", "Auxiliary", "Build")
  168. if isdir(path):
  169. return 15, path
  170. return None, None
  171. PLAT_SPEC_TO_RUNTIME = {
  172. 'x86': 'x86',
  173. 'x86_amd64': 'x64',
  174. 'x86_arm': 'arm',
  175. 'x86_arm64': 'arm64'
  176. }
  177. def _msvc14_find_vcvarsall(plat_spec):
  178. """Python 3.8 "distutils/_msvccompiler.py" backport"""
  179. _, best_dir = _msvc14_find_vc2017()
  180. vcruntime = None
  181. if plat_spec in PLAT_SPEC_TO_RUNTIME:
  182. vcruntime_plat = PLAT_SPEC_TO_RUNTIME[plat_spec]
  183. else:
  184. vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86'
  185. if best_dir:
  186. vcredist = join(best_dir, "..", "..", "redist", "MSVC", "**",
  187. vcruntime_plat, "Microsoft.VC14*.CRT",
  188. "vcruntime140.dll")
  189. try:
  190. import glob
  191. vcruntime = glob.glob(vcredist, recursive=True)[-1]
  192. except (ImportError, OSError, LookupError):
  193. vcruntime = None
  194. if not best_dir:
  195. best_version, best_dir = _msvc14_find_vc2015()
  196. if best_version:
  197. vcruntime = join(best_dir, 'redist', vcruntime_plat,
  198. "Microsoft.VC140.CRT", "vcruntime140.dll")
  199. if not best_dir:
  200. return None, None
  201. vcvarsall = join(best_dir, "vcvarsall.bat")
  202. if not isfile(vcvarsall):
  203. return None, None
  204. if not vcruntime or not isfile(vcruntime):
  205. vcruntime = None
  206. return vcvarsall, vcruntime
  207. def _msvc14_get_vc_env(plat_spec):
  208. """Python 3.8 "distutils/_msvccompiler.py" backport"""
  209. if "DISTUTILS_USE_SDK" in environ:
  210. return {
  211. key.lower(): value
  212. for key, value in environ.items()
  213. }
  214. vcvarsall, vcruntime = _msvc14_find_vcvarsall(plat_spec)
  215. if not vcvarsall:
  216. raise distutils.errors.DistutilsPlatformError(
  217. "Unable to find vcvarsall.bat"
  218. )
  219. try:
  220. out = subprocess.check_output(
  221. 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec),
  222. stderr=subprocess.STDOUT,
  223. ).decode('utf-16le', errors='replace')
  224. except subprocess.CalledProcessError as exc:
  225. raise distutils.errors.DistutilsPlatformError(
  226. "Error executing {}".format(exc.cmd)
  227. ) from exc
  228. env = {
  229. key.lower(): value
  230. for key, _, value in
  231. (line.partition('=') for line in out.splitlines())
  232. if key and value
  233. }
  234. if vcruntime:
  235. env['py_vcruntime_redist'] = vcruntime
  236. return env
  237. def msvc14_get_vc_env(plat_spec):
  238. """
  239. Patched "distutils._msvccompiler._get_vc_env" for support extra
  240. Microsoft Visual C++ 14.X compilers.
  241. Set environment without use of "vcvarsall.bat".
  242. Parameters
  243. ----------
  244. plat_spec: str
  245. Target architecture.
  246. Return
  247. ------
  248. dict
  249. environment
  250. """
  251. # Always use backport from CPython 3.8
  252. try:
  253. return _msvc14_get_vc_env(plat_spec)
  254. except distutils.errors.DistutilsPlatformError as exc:
  255. _augment_exception(exc, 14.0)
  256. raise
  257. def msvc14_gen_lib_options(*args, **kwargs):
  258. """
  259. Patched "distutils._msvccompiler.gen_lib_options" for fix
  260. compatibility between "numpy.distutils" and "distutils._msvccompiler"
  261. (for Numpy < 1.11.2)
  262. """
  263. if "numpy.distutils" in sys.modules:
  264. import numpy as np
  265. if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'):
  266. return np.distutils.ccompiler.gen_lib_options(*args, **kwargs)
  267. return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs)
  268. def _augment_exception(exc, version, arch=''):
  269. """
  270. Add details to the exception message to help guide the user
  271. as to what action will resolve it.
  272. """
  273. # Error if MSVC++ directory not found or environment not set
  274. message = exc.args[0]
  275. if "vcvarsall" in message.lower() or "visual c" in message.lower():
  276. # Special error message if MSVC++ not installed
  277. tmpl = 'Microsoft Visual C++ {version:0.1f} or greater is required.'
  278. message = tmpl.format(**locals())
  279. msdownload = 'www.microsoft.com/download/details.aspx?id=%d'
  280. if version == 9.0:
  281. if arch.lower().find('ia64') > -1:
  282. # For VC++ 9.0, if IA64 support is needed, redirect user
  283. # to Windows SDK 7.0.
  284. # Note: No download link available from Microsoft.
  285. message += ' Get it with "Microsoft Windows SDK 7.0"'
  286. else:
  287. # For VC++ 9.0 redirect user to Vc++ for Python 2.7 :
  288. # This redirection link is maintained by Microsoft.
  289. # Contact vspython@microsoft.com if it needs updating.
  290. message += ' Get it from http://aka.ms/vcpython27'
  291. elif version == 10.0:
  292. # For VC++ 10.0 Redirect user to Windows SDK 7.1
  293. message += ' Get it with "Microsoft Windows SDK 7.1": '
  294. message += msdownload % 8279
  295. elif version >= 14.0:
  296. # For VC++ 14.X Redirect user to latest Visual C++ Build Tools
  297. message += (' Get it with "Microsoft C++ Build Tools": '
  298. r'https://visualstudio.microsoft.com'
  299. r'/visual-cpp-build-tools/')
  300. exc.args = (message, )
  301. class PlatformInfo:
  302. """
  303. Current and Target Architectures information.
  304. Parameters
  305. ----------
  306. arch: str
  307. Target architecture.
  308. """
  309. current_cpu = environ.get('processor_architecture', '').lower()
  310. def __init__(self, arch):
  311. self.arch = arch.lower().replace('x64', 'amd64')
  312. @property
  313. def target_cpu(self):
  314. """
  315. Return Target CPU architecture.
  316. Return
  317. ------
  318. str
  319. Target CPU
  320. """
  321. return self.arch[self.arch.find('_') + 1:]
  322. def target_is_x86(self):
  323. """
  324. Return True if target CPU is x86 32 bits..
  325. Return
  326. ------
  327. bool
  328. CPU is x86 32 bits
  329. """
  330. return self.target_cpu == 'x86'
  331. def current_is_x86(self):
  332. """
  333. Return True if current CPU is x86 32 bits..
  334. Return
  335. ------
  336. bool
  337. CPU is x86 32 bits
  338. """
  339. return self.current_cpu == 'x86'
  340. def current_dir(self, hidex86=False, x64=False):
  341. """
  342. Current platform specific subfolder.
  343. Parameters
  344. ----------
  345. hidex86: bool
  346. return '' and not '\x86' if architecture is x86.
  347. x64: bool
  348. return '\x64' and not '\amd64' if architecture is amd64.
  349. Return
  350. ------
  351. str
  352. subfolder: '\target', or '' (see hidex86 parameter)
  353. """
  354. return (
  355. '' if (self.current_cpu == 'x86' and hidex86) else
  356. r'\x64' if (self.current_cpu == 'amd64' and x64) else
  357. r'\%s' % self.current_cpu
  358. )
  359. def target_dir(self, hidex86=False, x64=False):
  360. r"""
  361. Target platform specific subfolder.
  362. Parameters
  363. ----------
  364. hidex86: bool
  365. return '' and not '\x86' if architecture is x86.
  366. x64: bool
  367. return '\x64' and not '\amd64' if architecture is amd64.
  368. Return
  369. ------
  370. str
  371. subfolder: '\current', or '' (see hidex86 parameter)
  372. """
  373. return (
  374. '' if (self.target_cpu == 'x86' and hidex86) else
  375. r'\x64' if (self.target_cpu == 'amd64' and x64) else
  376. r'\%s' % self.target_cpu
  377. )
  378. def cross_dir(self, forcex86=False):
  379. r"""
  380. Cross platform specific subfolder.
  381. Parameters
  382. ----------
  383. forcex86: bool
  384. Use 'x86' as current architecture even if current architecture is
  385. not x86.
  386. Return
  387. ------
  388. str
  389. subfolder: '' if target architecture is current architecture,
  390. '\current_target' if not.
  391. """
  392. current = 'x86' if forcex86 else self.current_cpu
  393. return (
  394. '' if self.target_cpu == current else
  395. self.target_dir().replace('\\', '\\%s_' % current)
  396. )
  397. class RegistryInfo:
  398. """
  399. Microsoft Visual Studio related registry information.
  400. Parameters
  401. ----------
  402. platform_info: PlatformInfo
  403. "PlatformInfo" instance.
  404. """
  405. HKEYS = (winreg.HKEY_USERS,
  406. winreg.HKEY_CURRENT_USER,
  407. winreg.HKEY_LOCAL_MACHINE,
  408. winreg.HKEY_CLASSES_ROOT)
  409. def __init__(self, platform_info):
  410. self.pi = platform_info
  411. @property
  412. def visualstudio(self):
  413. """
  414. Microsoft Visual Studio root registry key.
  415. Return
  416. ------
  417. str
  418. Registry key
  419. """
  420. return 'VisualStudio'
  421. @property
  422. def sxs(self):
  423. """
  424. Microsoft Visual Studio SxS registry key.
  425. Return
  426. ------
  427. str
  428. Registry key
  429. """
  430. return join(self.visualstudio, 'SxS')
  431. @property
  432. def vc(self):
  433. """
  434. Microsoft Visual C++ VC7 registry key.
  435. Return
  436. ------
  437. str
  438. Registry key
  439. """
  440. return join(self.sxs, 'VC7')
  441. @property
  442. def vs(self):
  443. """
  444. Microsoft Visual Studio VS7 registry key.
  445. Return
  446. ------
  447. str
  448. Registry key
  449. """
  450. return join(self.sxs, 'VS7')
  451. @property
  452. def vc_for_python(self):
  453. """
  454. Microsoft Visual C++ for Python registry key.
  455. Return
  456. ------
  457. str
  458. Registry key
  459. """
  460. return r'DevDiv\VCForPython'
  461. @property
  462. def microsoft_sdk(self):
  463. """
  464. Microsoft SDK registry key.
  465. Return
  466. ------
  467. str
  468. Registry key
  469. """
  470. return 'Microsoft SDKs'
  471. @property
  472. def windows_sdk(self):
  473. """
  474. Microsoft Windows/Platform SDK registry key.
  475. Return
  476. ------
  477. str
  478. Registry key
  479. """
  480. return join(self.microsoft_sdk, 'Windows')
  481. @property
  482. def netfx_sdk(self):
  483. """
  484. Microsoft .NET Framework SDK registry key.
  485. Return
  486. ------
  487. str
  488. Registry key
  489. """
  490. return join(self.microsoft_sdk, 'NETFXSDK')
  491. @property
  492. def windows_kits_roots(self):
  493. """
  494. Microsoft Windows Kits Roots registry key.
  495. Return
  496. ------
  497. str
  498. Registry key
  499. """
  500. return r'Windows Kits\Installed Roots'
  501. def microsoft(self, key, x86=False):
  502. """
  503. Return key in Microsoft software registry.
  504. Parameters
  505. ----------
  506. key: str
  507. Registry key path where look.
  508. x86: str
  509. Force x86 software registry.
  510. Return
  511. ------
  512. str
  513. Registry key
  514. """
  515. node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node'
  516. return join('Software', node64, 'Microsoft', key)
  517. def lookup(self, key, name):
  518. """
  519. Look for values in registry in Microsoft software registry.
  520. Parameters
  521. ----------
  522. key: str
  523. Registry key path where look.
  524. name: str
  525. Value name to find.
  526. Return
  527. ------
  528. str
  529. value
  530. """
  531. key_read = winreg.KEY_READ
  532. openkey = winreg.OpenKey
  533. closekey = winreg.CloseKey
  534. ms = self.microsoft
  535. for hkey in self.HKEYS:
  536. bkey = None
  537. try:
  538. bkey = openkey(hkey, ms(key), 0, key_read)
  539. except (OSError, IOError):
  540. if not self.pi.current_is_x86():
  541. try:
  542. bkey = openkey(hkey, ms(key, True), 0, key_read)
  543. except (OSError, IOError):
  544. continue
  545. else:
  546. continue
  547. try:
  548. return winreg.QueryValueEx(bkey, name)[0]
  549. except (OSError, IOError):
  550. pass
  551. finally:
  552. if bkey:
  553. closekey(bkey)
  554. class SystemInfo:
  555. """
  556. Microsoft Windows and Visual Studio related system information.
  557. Parameters
  558. ----------
  559. registry_info: RegistryInfo
  560. "RegistryInfo" instance.
  561. vc_ver: float
  562. Required Microsoft Visual C++ version.
  563. """
  564. # Variables and properties in this class use originals CamelCase variables
  565. # names from Microsoft source files for more easy comparison.
  566. WinDir = environ.get('WinDir', '')
  567. ProgramFiles = environ.get('ProgramFiles', '')
  568. ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles)
  569. def __init__(self, registry_info, vc_ver=None):
  570. self.ri = registry_info
  571. self.pi = self.ri.pi
  572. self.known_vs_paths = self.find_programdata_vs_vers()
  573. # Except for VS15+, VC version is aligned with VS version
  574. self.vs_ver = self.vc_ver = (
  575. vc_ver or self._find_latest_available_vs_ver())
  576. def _find_latest_available_vs_ver(self):
  577. """
  578. Find the latest VC version
  579. Return
  580. ------
  581. float
  582. version
  583. """
  584. reg_vc_vers = self.find_reg_vs_vers()
  585. if not (reg_vc_vers or self.known_vs_paths):
  586. raise distutils.errors.DistutilsPlatformError(
  587. 'No Microsoft Visual C++ version found')
  588. vc_vers = set(reg_vc_vers)
  589. vc_vers.update(self.known_vs_paths)
  590. return sorted(vc_vers)[-1]
  591. def find_reg_vs_vers(self):
  592. """
  593. Find Microsoft Visual Studio versions available in registry.
  594. Return
  595. ------
  596. list of float
  597. Versions
  598. """
  599. ms = self.ri.microsoft
  600. vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs)
  601. vs_vers = []
  602. for hkey, key in itertools.product(self.ri.HKEYS, vckeys):
  603. try:
  604. bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ)
  605. except (OSError, IOError):
  606. continue
  607. with bkey:
  608. subkeys, values, _ = winreg.QueryInfoKey(bkey)
  609. for i in range(values):
  610. with contextlib.suppress(ValueError):
  611. ver = float(winreg.EnumValue(bkey, i)[0])
  612. if ver not in vs_vers:
  613. vs_vers.append(ver)
  614. for i in range(subkeys):
  615. with contextlib.suppress(ValueError):
  616. ver = float(winreg.EnumKey(bkey, i))
  617. if ver not in vs_vers:
  618. vs_vers.append(ver)
  619. return sorted(vs_vers)
  620. def find_programdata_vs_vers(self):
  621. r"""
  622. Find Visual studio 2017+ versions from information in
  623. "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances".
  624. Return
  625. ------
  626. dict
  627. float version as key, path as value.
  628. """
  629. vs_versions = {}
  630. instances_dir = \
  631. r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances'
  632. try:
  633. hashed_names = listdir(instances_dir)
  634. except (OSError, IOError):
  635. # Directory not exists with all Visual Studio versions
  636. return vs_versions
  637. for name in hashed_names:
  638. try:
  639. # Get VS installation path from "state.json" file
  640. state_path = join(instances_dir, name, 'state.json')
  641. with open(state_path, 'rt', encoding='utf-8') as state_file:
  642. state = json.load(state_file)
  643. vs_path = state['installationPath']
  644. # Raises OSError if this VS installation does not contain VC
  645. listdir(join(vs_path, r'VC\Tools\MSVC'))
  646. # Store version and path
  647. vs_versions[self._as_float_version(
  648. state['installationVersion'])] = vs_path
  649. except (OSError, IOError, KeyError):
  650. # Skip if "state.json" file is missing or bad format
  651. continue
  652. return vs_versions
  653. @staticmethod
  654. def _as_float_version(version):
  655. """
  656. Return a string version as a simplified float version (major.minor)
  657. Parameters
  658. ----------
  659. version: str
  660. Version.
  661. Return
  662. ------
  663. float
  664. version
  665. """
  666. return float('.'.join(version.split('.')[:2]))
  667. @property
  668. def VSInstallDir(self):
  669. """
  670. Microsoft Visual Studio directory.
  671. Return
  672. ------
  673. str
  674. path
  675. """
  676. # Default path
  677. default = join(self.ProgramFilesx86,
  678. 'Microsoft Visual Studio %0.1f' % self.vs_ver)
  679. # Try to get path from registry, if fail use default path
  680. return self.ri.lookup(self.ri.vs, '%0.1f' % self.vs_ver) or default
  681. @property
  682. def VCInstallDir(self):
  683. """
  684. Microsoft Visual C++ directory.
  685. Return
  686. ------
  687. str
  688. path
  689. """
  690. path = self._guess_vc() or self._guess_vc_legacy()
  691. if not isdir(path):
  692. msg = 'Microsoft Visual C++ directory not found'
  693. raise distutils.errors.DistutilsPlatformError(msg)
  694. return path
  695. def _guess_vc(self):
  696. """
  697. Locate Visual C++ for VS2017+.
  698. Return
  699. ------
  700. str
  701. path
  702. """
  703. if self.vs_ver <= 14.0:
  704. return ''
  705. try:
  706. # First search in known VS paths
  707. vs_dir = self.known_vs_paths[self.vs_ver]
  708. except KeyError:
  709. # Else, search with path from registry
  710. vs_dir = self.VSInstallDir
  711. guess_vc = join(vs_dir, r'VC\Tools\MSVC')
  712. # Subdir with VC exact version as name
  713. try:
  714. # Update the VC version with real one instead of VS version
  715. vc_ver = listdir(guess_vc)[-1]
  716. self.vc_ver = self._as_float_version(vc_ver)
  717. return join(guess_vc, vc_ver)
  718. except (OSError, IOError, IndexError):
  719. return ''
  720. def _guess_vc_legacy(self):
  721. """
  722. Locate Visual C++ for versions prior to 2017.
  723. Return
  724. ------
  725. str
  726. path
  727. """
  728. default = join(self.ProgramFilesx86,
  729. r'Microsoft Visual Studio %0.1f\VC' % self.vs_ver)
  730. # Try to get "VC++ for Python" path from registry as default path
  731. reg_path = join(self.ri.vc_for_python, '%0.1f' % self.vs_ver)
  732. python_vc = self.ri.lookup(reg_path, 'installdir')
  733. default_vc = join(python_vc, 'VC') if python_vc else default
  734. # Try to get path from registry, if fail use default path
  735. return self.ri.lookup(self.ri.vc, '%0.1f' % self.vs_ver) or default_vc
  736. @property
  737. def WindowsSdkVersion(self):
  738. """
  739. Microsoft Windows SDK versions for specified MSVC++ version.
  740. Return
  741. ------
  742. tuple of str
  743. versions
  744. """
  745. if self.vs_ver <= 9.0:
  746. return '7.0', '6.1', '6.0a'
  747. elif self.vs_ver == 10.0:
  748. return '7.1', '7.0a'
  749. elif self.vs_ver == 11.0:
  750. return '8.0', '8.0a'
  751. elif self.vs_ver == 12.0:
  752. return '8.1', '8.1a'
  753. elif self.vs_ver >= 14.0:
  754. return '10.0', '8.1'
  755. @property
  756. def WindowsSdkLastVersion(self):
  757. """
  758. Microsoft Windows SDK last version.
  759. Return
  760. ------
  761. str
  762. version
  763. """
  764. return self._use_last_dir_name(join(self.WindowsSdkDir, 'lib'))
  765. @property # noqa: C901
  766. def WindowsSdkDir(self): # noqa: C901 # is too complex (12) # FIXME
  767. """
  768. Microsoft Windows SDK directory.
  769. Return
  770. ------
  771. str
  772. path
  773. """
  774. sdkdir = ''
  775. for ver in self.WindowsSdkVersion:
  776. # Try to get it from registry
  777. loc = join(self.ri.windows_sdk, 'v%s' % ver)
  778. sdkdir = self.ri.lookup(loc, 'installationfolder')
  779. if sdkdir:
  780. break
  781. if not sdkdir or not isdir(sdkdir):
  782. # Try to get "VC++ for Python" version from registry
  783. path = join(self.ri.vc_for_python, '%0.1f' % self.vc_ver)
  784. install_base = self.ri.lookup(path, 'installdir')
  785. if install_base:
  786. sdkdir = join(install_base, 'WinSDK')
  787. if not sdkdir or not isdir(sdkdir):
  788. # If fail, use default new path
  789. for ver in self.WindowsSdkVersion:
  790. intver = ver[:ver.rfind('.')]
  791. path = r'Microsoft SDKs\Windows Kits\%s' % intver
  792. d = join(self.ProgramFiles, path)
  793. if isdir(d):
  794. sdkdir = d
  795. if not sdkdir or not isdir(sdkdir):
  796. # If fail, use default old path
  797. for ver in self.WindowsSdkVersion:
  798. path = r'Microsoft SDKs\Windows\v%s' % ver
  799. d = join(self.ProgramFiles, path)
  800. if isdir(d):
  801. sdkdir = d
  802. if not sdkdir:
  803. # If fail, use Platform SDK
  804. sdkdir = join(self.VCInstallDir, 'PlatformSDK')
  805. return sdkdir
  806. @property
  807. def WindowsSDKExecutablePath(self):
  808. """
  809. Microsoft Windows SDK executable directory.
  810. Return
  811. ------
  812. str
  813. path
  814. """
  815. # Find WinSDK NetFx Tools registry dir name
  816. if self.vs_ver <= 11.0:
  817. netfxver = 35
  818. arch = ''
  819. else:
  820. netfxver = 40
  821. hidex86 = True if self.vs_ver <= 12.0 else False
  822. arch = self.pi.current_dir(x64=True, hidex86=hidex86)
  823. fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-'))
  824. # list all possibles registry paths
  825. regpaths = []
  826. if self.vs_ver >= 14.0:
  827. for ver in self.NetFxSdkVersion:
  828. regpaths += [join(self.ri.netfx_sdk, ver, fx)]
  829. for ver in self.WindowsSdkVersion:
  830. regpaths += [join(self.ri.windows_sdk, 'v%sA' % ver, fx)]
  831. # Return installation folder from the more recent path
  832. for path in regpaths:
  833. execpath = self.ri.lookup(path, 'installationfolder')
  834. if execpath:
  835. return execpath
  836. @property
  837. def FSharpInstallDir(self):
  838. """
  839. Microsoft Visual F# directory.
  840. Return
  841. ------
  842. str
  843. path
  844. """
  845. path = join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver)
  846. return self.ri.lookup(path, 'productdir') or ''
  847. @property
  848. def UniversalCRTSdkDir(self):
  849. """
  850. Microsoft Universal CRT SDK directory.
  851. Return
  852. ------
  853. str
  854. path
  855. """
  856. # Set Kit Roots versions for specified MSVC++ version
  857. vers = ('10', '81') if self.vs_ver >= 14.0 else ()
  858. # Find path of the more recent Kit
  859. for ver in vers:
  860. sdkdir = self.ri.lookup(self.ri.windows_kits_roots,
  861. 'kitsroot%s' % ver)
  862. if sdkdir:
  863. return sdkdir or ''
  864. @property
  865. def UniversalCRTSdkLastVersion(self):
  866. """
  867. Microsoft Universal C Runtime SDK last version.
  868. Return
  869. ------
  870. str
  871. version
  872. """
  873. return self._use_last_dir_name(join(self.UniversalCRTSdkDir, 'lib'))
  874. @property
  875. def NetFxSdkVersion(self):
  876. """
  877. Microsoft .NET Framework SDK versions.
  878. Return
  879. ------
  880. tuple of str
  881. versions
  882. """
  883. # Set FxSdk versions for specified VS version
  884. return (('4.7.2', '4.7.1', '4.7',
  885. '4.6.2', '4.6.1', '4.6',
  886. '4.5.2', '4.5.1', '4.5')
  887. if self.vs_ver >= 14.0 else ())
  888. @property
  889. def NetFxSdkDir(self):
  890. """
  891. Microsoft .NET Framework SDK directory.
  892. Return
  893. ------
  894. str
  895. path
  896. """
  897. sdkdir = ''
  898. for ver in self.NetFxSdkVersion:
  899. loc = join(self.ri.netfx_sdk, ver)
  900. sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder')
  901. if sdkdir:
  902. break
  903. return sdkdir
  904. @property
  905. def FrameworkDir32(self):
  906. """
  907. Microsoft .NET Framework 32bit directory.
  908. Return
  909. ------
  910. str
  911. path
  912. """
  913. # Default path
  914. guess_fw = join(self.WinDir, r'Microsoft.NET\Framework')
  915. # Try to get path from registry, if fail use default path
  916. return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw
  917. @property
  918. def FrameworkDir64(self):
  919. """
  920. Microsoft .NET Framework 64bit directory.
  921. Return
  922. ------
  923. str
  924. path
  925. """
  926. # Default path
  927. guess_fw = join(self.WinDir, r'Microsoft.NET\Framework64')
  928. # Try to get path from registry, if fail use default path
  929. return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw
  930. @property
  931. def FrameworkVersion32(self):
  932. """
  933. Microsoft .NET Framework 32bit versions.
  934. Return
  935. ------
  936. tuple of str
  937. versions
  938. """
  939. return self._find_dot_net_versions(32)
  940. @property
  941. def FrameworkVersion64(self):
  942. """
  943. Microsoft .NET Framework 64bit versions.
  944. Return
  945. ------
  946. tuple of str
  947. versions
  948. """
  949. return self._find_dot_net_versions(64)
  950. def _find_dot_net_versions(self, bits):
  951. """
  952. Find Microsoft .NET Framework versions.
  953. Parameters
  954. ----------
  955. bits: int
  956. Platform number of bits: 32 or 64.
  957. Return
  958. ------
  959. tuple of str
  960. versions
  961. """
  962. # Find actual .NET version in registry
  963. reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits)
  964. dot_net_dir = getattr(self, 'FrameworkDir%d' % bits)
  965. ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or ''
  966. # Set .NET versions for specified MSVC++ version
  967. if self.vs_ver >= 12.0:
  968. return ver, 'v4.0'
  969. elif self.vs_ver >= 10.0:
  970. return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5'
  971. elif self.vs_ver == 9.0:
  972. return 'v3.5', 'v2.0.50727'
  973. elif self.vs_ver == 8.0:
  974. return 'v3.0', 'v2.0.50727'
  975. @staticmethod
  976. def _use_last_dir_name(path, prefix=''):
  977. """
  978. Return name of the last dir in path or '' if no dir found.
  979. Parameters
  980. ----------
  981. path: str
  982. Use dirs in this path
  983. prefix: str
  984. Use only dirs starting by this prefix
  985. Return
  986. ------
  987. str
  988. name
  989. """
  990. matching_dirs = (
  991. dir_name
  992. for dir_name in reversed(listdir(path))
  993. if isdir(join(path, dir_name)) and
  994. dir_name.startswith(prefix)
  995. )
  996. return next(matching_dirs, None) or ''
  997. class EnvironmentInfo:
  998. """
  999. Return environment variables for specified Microsoft Visual C++ version
  1000. and platform : Lib, Include, Path and libpath.
  1001. This function is compatible with Microsoft Visual C++ 9.0 to 14.X.
  1002. Script created by analysing Microsoft environment configuration files like
  1003. "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ...
  1004. Parameters
  1005. ----------
  1006. arch: str
  1007. Target architecture.
  1008. vc_ver: float
  1009. Required Microsoft Visual C++ version. If not set, autodetect the last
  1010. version.
  1011. vc_min_ver: float
  1012. Minimum Microsoft Visual C++ version.
  1013. """
  1014. # Variables and properties in this class use originals CamelCase variables
  1015. # names from Microsoft source files for more easy comparison.
  1016. def __init__(self, arch, vc_ver=None, vc_min_ver=0):
  1017. self.pi = PlatformInfo(arch)
  1018. self.ri = RegistryInfo(self.pi)
  1019. self.si = SystemInfo(self.ri, vc_ver)
  1020. if self.vc_ver < vc_min_ver:
  1021. err = 'No suitable Microsoft Visual C++ version found'
  1022. raise distutils.errors.DistutilsPlatformError(err)
  1023. @property
  1024. def vs_ver(self):
  1025. """
  1026. Microsoft Visual Studio.
  1027. Return
  1028. ------
  1029. float
  1030. version
  1031. """
  1032. return self.si.vs_ver
  1033. @property
  1034. def vc_ver(self):
  1035. """
  1036. Microsoft Visual C++ version.
  1037. Return
  1038. ------
  1039. float
  1040. version
  1041. """
  1042. return self.si.vc_ver
  1043. @property
  1044. def VSTools(self):
  1045. """
  1046. Microsoft Visual Studio Tools.
  1047. Return
  1048. ------
  1049. list of str
  1050. paths
  1051. """
  1052. paths = [r'Common7\IDE', r'Common7\Tools']
  1053. if self.vs_ver >= 14.0:
  1054. arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
  1055. paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow']
  1056. paths += [r'Team Tools\Performance Tools']
  1057. paths += [r'Team Tools\Performance Tools%s' % arch_subdir]
  1058. return [join(self.si.VSInstallDir, path) for path in paths]
  1059. @property
  1060. def VCIncludes(self):
  1061. """
  1062. Microsoft Visual C++ & Microsoft Foundation Class Includes.
  1063. Return
  1064. ------
  1065. list of str
  1066. paths
  1067. """
  1068. return [join(self.si.VCInstallDir, 'Include'),
  1069. join(self.si.VCInstallDir, r'ATLMFC\Include')]
  1070. @property
  1071. def VCLibraries(self):
  1072. """
  1073. Microsoft Visual C++ & Microsoft Foundation Class Libraries.
  1074. Return
  1075. ------
  1076. list of str
  1077. paths
  1078. """
  1079. if self.vs_ver >= 15.0:
  1080. arch_subdir = self.pi.target_dir(x64=True)
  1081. else:
  1082. arch_subdir = self.pi.target_dir(hidex86=True)
  1083. paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir]
  1084. if self.vs_ver >= 14.0:
  1085. paths += [r'Lib\store%s' % arch_subdir]
  1086. return [join(self.si.VCInstallDir, path) for path in paths]
  1087. @property
  1088. def VCStoreRefs(self):
  1089. """
  1090. Microsoft Visual C++ store references Libraries.
  1091. Return
  1092. ------
  1093. list of str
  1094. paths
  1095. """
  1096. if self.vs_ver < 14.0:
  1097. return []
  1098. return [join(self.si.VCInstallDir, r'Lib\store\references')]
  1099. @property
  1100. def VCTools(self):
  1101. """
  1102. Microsoft Visual C++ Tools.
  1103. Return
  1104. ------
  1105. list of str
  1106. paths
  1107. """
  1108. si = self.si
  1109. tools = [join(si.VCInstallDir, 'VCPackages')]
  1110. forcex86 = True if self.vs_ver <= 10.0 else False
  1111. arch_subdir = self.pi.cross_dir(forcex86)
  1112. if arch_subdir:
  1113. tools += [join(si.VCInstallDir, 'Bin%s' % arch_subdir)]
  1114. if self.vs_ver == 14.0:
  1115. path = 'Bin%s' % self.pi.current_dir(hidex86=True)
  1116. tools += [join(si.VCInstallDir, path)]
  1117. elif self.vs_ver >= 15.0:
  1118. host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else
  1119. r'bin\HostX64%s')
  1120. tools += [join(
  1121. si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))]
  1122. if self.pi.current_cpu != self.pi.target_cpu:
  1123. tools += [join(
  1124. si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))]
  1125. else:
  1126. tools += [join(si.VCInstallDir, 'Bin')]
  1127. return tools
  1128. @property
  1129. def OSLibraries(self):
  1130. """
  1131. Microsoft Windows SDK Libraries.
  1132. Return
  1133. ------
  1134. list of str
  1135. paths
  1136. """
  1137. if self.vs_ver <= 10.0:
  1138. arch_subdir = self.pi.target_dir(hidex86=True, x64=True)
  1139. return [join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)]
  1140. else:
  1141. arch_subdir = self.pi.target_dir(x64=True)
  1142. lib = join(self.si.WindowsSdkDir, 'lib')
  1143. libver = self._sdk_subdir
  1144. return [join(lib, '%sum%s' % (libver, arch_subdir))]
  1145. @property
  1146. def OSIncludes(self):
  1147. """
  1148. Microsoft Windows SDK Include.
  1149. Return
  1150. ------
  1151. list of str
  1152. paths
  1153. """
  1154. include = join(self.si.WindowsSdkDir, 'include')
  1155. if self.vs_ver <= 10.0:
  1156. return [include, join(include, 'gl')]
  1157. else:
  1158. if self.vs_ver >= 14.0:
  1159. sdkver = self._sdk_subdir
  1160. else:
  1161. sdkver = ''
  1162. return [join(include, '%sshared' % sdkver),
  1163. join(include, '%sum' % sdkver),
  1164. join(include, '%swinrt' % sdkver)]
  1165. @property
  1166. def OSLibpath(self):
  1167. """
  1168. Microsoft Windows SDK Libraries Paths.
  1169. Return
  1170. ------
  1171. list of str
  1172. paths
  1173. """
  1174. ref = join(self.si.WindowsSdkDir, 'References')
  1175. libpath = []
  1176. if self.vs_ver <= 9.0:
  1177. libpath += self.OSLibraries
  1178. if self.vs_ver >= 11.0:
  1179. libpath += [join(ref, r'CommonConfiguration\Neutral')]
  1180. if self.vs_ver >= 14.0:
  1181. libpath += [
  1182. ref,
  1183. join(self.si.WindowsSdkDir, 'UnionMetadata'),
  1184. join(
  1185. ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),
  1186. join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'),
  1187. join(
  1188. ref, 'Windows.Networking.Connectivity.WwanContract',
  1189. '1.0.0.0'),
  1190. join(
  1191. self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs',
  1192. '%0.1f' % self.vs_ver, 'References', 'CommonConfiguration',
  1193. 'neutral'),
  1194. ]
  1195. return libpath
  1196. @property
  1197. def SdkTools(self):
  1198. """
  1199. Microsoft Windows SDK Tools.
  1200. Return
  1201. ------
  1202. list of str
  1203. paths
  1204. """
  1205. return list(self._sdk_tools())
  1206. def _sdk_tools(self):
  1207. """
  1208. Microsoft Windows SDK Tools paths generator.
  1209. Return
  1210. ------
  1211. generator of str
  1212. paths
  1213. """
  1214. if self.vs_ver < 15.0:
  1215. bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86'
  1216. yield join(self.si.WindowsSdkDir, bin_dir)
  1217. if not self.pi.current_is_x86():
  1218. arch_subdir = self.pi.current_dir(x64=True)
  1219. path = 'Bin%s' % arch_subdir
  1220. yield join(self.si.WindowsSdkDir, path)
  1221. if self.vs_ver in (10.0, 11.0):
  1222. if self.pi.target_is_x86():
  1223. arch_subdir = ''
  1224. else:
  1225. arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
  1226. path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir
  1227. yield join(self.si.WindowsSdkDir, path)
  1228. elif self.vs_ver >= 15.0:
  1229. path = join(self.si.WindowsSdkDir, 'Bin')
  1230. arch_subdir = self.pi.current_dir(x64=True)
  1231. sdkver = self.si.WindowsSdkLastVersion
  1232. yield join(path, '%s%s' % (sdkver, arch_subdir))
  1233. if self.si.WindowsSDKExecutablePath:
  1234. yield self.si.WindowsSDKExecutablePath
  1235. @property
  1236. def _sdk_subdir(self):
  1237. """
  1238. Microsoft Windows SDK version subdir.
  1239. Return
  1240. ------
  1241. str
  1242. subdir
  1243. """
  1244. ucrtver = self.si.WindowsSdkLastVersion
  1245. return ('%s\\' % ucrtver) if ucrtver else ''
  1246. @property
  1247. def SdkSetup(self):
  1248. """
  1249. Microsoft Windows SDK Setup.
  1250. Return
  1251. ------
  1252. list of str
  1253. paths
  1254. """
  1255. if self.vs_ver > 9.0:
  1256. return []
  1257. return [join(self.si.WindowsSdkDir, 'Setup')]
  1258. @property
  1259. def FxTools(self):
  1260. """
  1261. Microsoft .NET Framework Tools.
  1262. Return
  1263. ------
  1264. list of str
  1265. paths
  1266. """
  1267. pi = self.pi
  1268. si = self.si
  1269. if self.vs_ver <= 10.0:
  1270. include32 = True
  1271. include64 = not pi.target_is_x86() and not pi.current_is_x86()
  1272. else:
  1273. include32 = pi.target_is_x86() or pi.current_is_x86()
  1274. include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64'
  1275. tools = []
  1276. if include32:
  1277. tools += [join(si.FrameworkDir32, ver)
  1278. for ver in si.FrameworkVersion32]
  1279. if include64:
  1280. tools += [join(si.FrameworkDir64, ver)
  1281. for ver in si.FrameworkVersion64]
  1282. return tools
  1283. @property
  1284. def NetFxSDKLibraries(self):
  1285. """
  1286. Microsoft .Net Framework SDK Libraries.
  1287. Return
  1288. ------
  1289. list of str
  1290. paths
  1291. """
  1292. if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
  1293. return []
  1294. arch_subdir = self.pi.target_dir(x64=True)
  1295. return [join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)]
  1296. @property
  1297. def NetFxSDKIncludes(self):
  1298. """
  1299. Microsoft .Net Framework SDK Includes.
  1300. Return
  1301. ------
  1302. list of str
  1303. paths
  1304. """
  1305. if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
  1306. return []
  1307. return [join(self.si.NetFxSdkDir, r'include\um')]
  1308. @property
  1309. def VsTDb(self):
  1310. """
  1311. Microsoft Visual Studio Team System Database.
  1312. Return
  1313. ------
  1314. list of str
  1315. paths
  1316. """
  1317. return [join(self.si.VSInstallDir, r'VSTSDB\Deploy')]
  1318. @property
  1319. def MSBuild(self):
  1320. """
  1321. Microsoft Build Engine.
  1322. Return
  1323. ------
  1324. list of str
  1325. paths
  1326. """
  1327. if self.vs_ver < 12.0:
  1328. return []
  1329. elif self.vs_ver < 15.0:
  1330. base_path = self.si.ProgramFilesx86
  1331. arch_subdir = self.pi.current_dir(hidex86=True)
  1332. else:
  1333. base_path = self.si.VSInstallDir
  1334. arch_subdir = ''
  1335. path = r'MSBuild\%0.1f\bin%s' % (self.vs_ver, arch_subdir)
  1336. build = [join(base_path, path)]
  1337. if self.vs_ver >= 15.0:
  1338. # Add Roslyn C# & Visual Basic Compiler
  1339. build += [join(base_path, path, 'Roslyn')]
  1340. return build
  1341. @property
  1342. def HTMLHelpWorkshop(self):
  1343. """
  1344. Microsoft HTML Help Workshop.
  1345. Return
  1346. ------
  1347. list of str
  1348. paths
  1349. """
  1350. if self.vs_ver < 11.0:
  1351. return []
  1352. return [join(self.si.ProgramFilesx86, 'HTML Help Workshop')]
  1353. @property
  1354. def UCRTLibraries(self):
  1355. """
  1356. Microsoft Universal C Runtime SDK Libraries.
  1357. Return
  1358. ------
  1359. list of str
  1360. paths
  1361. """
  1362. if self.vs_ver < 14.0:
  1363. return []
  1364. arch_subdir = self.pi.target_dir(x64=True)
  1365. lib = join(self.si.UniversalCRTSdkDir, 'lib')
  1366. ucrtver = self._ucrt_subdir
  1367. return [join(lib, '%sucrt%s' % (ucrtver, arch_subdir))]
  1368. @property
  1369. def UCRTIncludes(self):
  1370. """
  1371. Microsoft Universal C Runtime SDK Include.
  1372. Return
  1373. ------
  1374. list of str
  1375. paths
  1376. """
  1377. if self.vs_ver < 14.0:
  1378. return []
  1379. include = join(self.si.UniversalCRTSdkDir, 'include')
  1380. return [join(include, '%sucrt' % self._ucrt_subdir)]
  1381. @property
  1382. def _ucrt_subdir(self):
  1383. """
  1384. Microsoft Universal C Runtime SDK version subdir.
  1385. Return
  1386. ------
  1387. str
  1388. subdir
  1389. """
  1390. ucrtver = self.si.UniversalCRTSdkLastVersion
  1391. return ('%s\\' % ucrtver) if ucrtver else ''
  1392. @property
  1393. def FSharp(self):
  1394. """
  1395. Microsoft Visual F#.
  1396. Return
  1397. ------
  1398. list of str
  1399. paths
  1400. """
  1401. if 11.0 > self.vs_ver > 12.0:
  1402. return []
  1403. return [self.si.FSharpInstallDir]
  1404. @property
  1405. def VCRuntimeRedist(self):
  1406. """
  1407. Microsoft Visual C++ runtime redistributable dll.
  1408. Return
  1409. ------
  1410. str
  1411. path
  1412. """
  1413. vcruntime = 'vcruntime%d0.dll' % self.vc_ver
  1414. arch_subdir = self.pi.target_dir(x64=True).strip('\\')
  1415. # Installation prefixes candidates
  1416. prefixes = []
  1417. tools_path = self.si.VCInstallDir
  1418. redist_path = dirname(tools_path.replace(r'\Tools', r'\Redist'))
  1419. if isdir(redist_path):
  1420. # Redist version may not be exactly the same as tools
  1421. redist_path = join(redist_path, listdir(redist_path)[-1])
  1422. prefixes += [redist_path, join(redist_path, 'onecore')]
  1423. prefixes += [join(tools_path, 'redist')] # VS14 legacy path
  1424. # CRT directory
  1425. crt_dirs = ('Microsoft.VC%d.CRT' % (self.vc_ver * 10),
  1426. # Sometime store in directory with VS version instead of VC
  1427. 'Microsoft.VC%d.CRT' % (int(self.vs_ver) * 10))
  1428. # vcruntime path
  1429. for prefix, crt_dir in itertools.product(prefixes, crt_dirs):
  1430. path = join(prefix, arch_subdir, crt_dir, vcruntime)
  1431. if isfile(path):
  1432. return path
  1433. def return_env(self, exists=True):
  1434. """
  1435. Return environment dict.
  1436. Parameters
  1437. ----------
  1438. exists: bool
  1439. It True, only return existing paths.
  1440. Return
  1441. ------
  1442. dict
  1443. environment
  1444. """
  1445. env = dict(
  1446. include=self._build_paths('include',
  1447. [self.VCIncludes,
  1448. self.OSIncludes,
  1449. self.UCRTIncludes,
  1450. self.NetFxSDKIncludes],
  1451. exists),
  1452. lib=self._build_paths('lib',
  1453. [self.VCLibraries,
  1454. self.OSLibraries,
  1455. self.FxTools,
  1456. self.UCRTLibraries,
  1457. self.NetFxSDKLibraries],
  1458. exists),
  1459. libpath=self._build_paths('libpath',
  1460. [self.VCLibraries,
  1461. self.FxTools,
  1462. self.VCStoreRefs,
  1463. self.OSLibpath],
  1464. exists),
  1465. path=self._build_paths('path',
  1466. [self.VCTools,
  1467. self.VSTools,
  1468. self.VsTDb,
  1469. self.SdkTools,
  1470. self.SdkSetup,
  1471. self.FxTools,
  1472. self.MSBuild,
  1473. self.HTMLHelpWorkshop,
  1474. self.FSharp],
  1475. exists),
  1476. )
  1477. if self.vs_ver >= 14 and isfile(self.VCRuntimeRedist):
  1478. env['py_vcruntime_redist'] = self.VCRuntimeRedist
  1479. return env
  1480. def _build_paths(self, name, spec_path_lists, exists):
  1481. """
  1482. Given an environment variable name and specified paths,
  1483. return a pathsep-separated string of paths containing
  1484. unique, extant, directories from those paths and from
  1485. the environment variable. Raise an error if no paths
  1486. are resolved.
  1487. Parameters
  1488. ----------
  1489. name: str
  1490. Environment variable name
  1491. spec_path_lists: list of str
  1492. Paths
  1493. exists: bool
  1494. It True, only return existing paths.
  1495. Return
  1496. ------
  1497. str
  1498. Pathsep-separated paths
  1499. """
  1500. # flatten spec_path_lists
  1501. spec_paths = itertools.chain.from_iterable(spec_path_lists)
  1502. env_paths = environ.get(name, '').split(pathsep)
  1503. paths = itertools.chain(spec_paths, env_paths)
  1504. extant_paths = list(filter(isdir, paths)) if exists else paths
  1505. if not extant_paths:
  1506. msg = "%s environment variable is empty" % name.upper()
  1507. raise distutils.errors.DistutilsPlatformError(msg)
  1508. unique_paths = self._unique_everseen(extant_paths)
  1509. return pathsep.join(unique_paths)
  1510. # from Python docs
  1511. @staticmethod
  1512. def _unique_everseen(iterable, key=None):
  1513. """
  1514. List unique elements, preserving order.
  1515. Remember all elements ever seen.
  1516. _unique_everseen('AAAABBBCCDAABBB') --> A B C D
  1517. _unique_everseen('ABBCcAD', str.lower) --> A B C D
  1518. """
  1519. seen = set()
  1520. seen_add = seen.add
  1521. if key is None:
  1522. for element in itertools.filterfalse(seen.__contains__, iterable):
  1523. seen_add(element)
  1524. yield element
  1525. else:
  1526. for element in iterable:
  1527. k = key(element)
  1528. if k not in seen:
  1529. seen_add(k)
  1530. yield element