msvc.py 50 KB

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