msvc.py 50 KB

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