msvc.py 46 KB

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