msvc.py 46 KB

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