You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1806 lines
49KB

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