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.

1825 lines
50 KiB

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