msvc.py 47 KB

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